You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/01/09 13:55:57 UTC
(skywalking) branch master updated: Replace BanyanDB Java client with native implementation. (#13655)
This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git
The following commit(s) were added to refs/heads/master by this push:
new 05f70316db Replace BanyanDB Java client with native implementation. (#13655)
05f70316db is described below
commit 05f70316db02230566b783b7ebcd4aa2ce132906
Author: Wan Kai <wa...@foxmail.com>
AuthorDate: Fri Jan 9 21:55:52 2026 +0800
Replace BanyanDB Java client with native implementation. (#13655)
---
.gitmodules | 3 +
apm-dist/src/main/assembly/binary.xml | 1 -
docs/en/changes/changes.md | 3 +
docs/en/setup/backend/backend-setup.md | 7 +-
docs/en/setup/backend/configuration-vocabulary.md | 35 +-
docs/en/setup/backend/storages/banyandb.md | 24 +-
.../profiling/trace/ProfileTaskQueryService.java | 13 +-
.../server-library/library-banyandb-client/pom.xml | 143 +++
.../banyandb/v1/client/AbstractCriteria.java} | 22 +-
.../library/banyandb/v1/client/AbstractQuery.java | 239 +++++
.../library/banyandb/v1/client/AbstractWrite.java | 67 ++
.../skywalking/library/banyandb/v1/client/And.java | 55 +
.../library/banyandb/v1/client/BanyanDBClient.java | 1114 ++++++++++++++++++++
.../library/banyandb/v1/client/DataPoint.java | 66 ++
.../library/banyandb/v1/client/Element.java} | 28 +-
.../library/banyandb/v1/client/MeasureQuery.java | 238 +++++
.../banyandb/v1/client/MeasureQueryResponse.java | 51 +
.../library/banyandb/v1/client/MeasureWrite.java | 153 +++
.../library/banyandb/v1/client/Options.java | 86 ++
.../skywalking/library/banyandb/v1/client/Or.java | 55 +
.../banyandb/v1/client/PairQueryCondition.java | 350 ++++++
.../library/banyandb/v1/client/PropertyQuery.java | 101 ++
.../library/banyandb/v1/client/PropertyStore.java | 66 ++
.../library/banyandb/v1/client/RowEntity.java | 80 ++
.../library/banyandb/v1/client/Span.java | 57 +
.../library/banyandb/v1/client/StreamQuery.java | 102 ++
.../banyandb/v1/client/StreamQueryResponse.java | 49 +
.../library/banyandb/v1/client/StreamWrite.java | 149 +++
.../library/banyandb/v1/client/Tag.java} | 30 +-
.../library/banyandb/v1/client/TagAndValue.java | 235 +++++
.../library/banyandb/v1/client/TimestampRange.java | 59 ++
.../library/banyandb/v1/client/TopNQuery.java | 79 ++
.../banyandb/v1/client/TopNQueryResponse.java | 79 ++
.../library/banyandb/v1/client/Trace.java} | 34 +-
.../library/banyandb/v1/client/TraceQuery.java | 96 ++
.../banyandb/v1/client/TraceQueryResponse.java | 69 ++
.../library/banyandb/v1/client/TraceWrite.java | 153 +++
.../library/banyandb/v1/client/Value.java | 338 ++++++
.../banyandb/v1/client/auth/AuthInterceptor.java | 60 ++
.../v1/client/grpc/HandleExceptionsWith.java | 55 +
.../banyandb/v1/client/grpc/MetadataClient.java | 99 ++
.../v1/client/grpc/channel/ChannelFactory.java} | 21 +-
.../v1/client/grpc/channel/ChannelManager.java | 220 ++++
.../grpc/channel/ChannelManagerSettings.java} | 19 +-
.../client/grpc/channel/DefaultChannelFactory.java | 70 ++
.../client/grpc/exception/AbortedException.java} | 22 +-
.../grpc/exception/AlreadyExistsException.java} | 22 +-
.../exception/BanyanDBApiExceptionFactory.java | 64 ++
.../client/grpc/exception/BanyanDBException.java} | 32 +-
.../exception/BanyanDBGrpcApiExceptionFactory.java | 54 +
.../client/grpc/exception/CancelledException.java} | 23 +-
.../client/grpc/exception/DataLossException.java} | 22 +-
.../grpc/exception/DeadlineExceededException.java} | 22 +-
.../exception/FailedPreconditionException.java} | 22 +-
.../client/grpc/exception/InternalException.java} | 22 +-
.../grpc/exception/InvalidArgumentException.java} | 22 +-
.../grpc/exception/InvalidReferenceException.java} | 27 +-
.../client/grpc/exception/NotFoundException.java} | 22 +-
.../grpc/exception/OutOfRangeException.java} | 22 +-
.../grpc/exception/PermissionDeniedException.java} | 22 +-
.../exception/ResourceExhaustedException.java} | 22 +-
.../grpc/exception/UnauthenticatedException.java} | 22 +-
.../grpc/exception/UnavailableException.java} | 22 +-
.../grpc/exception/UnimplementedException.java} | 22 +-
.../client/grpc/exception/UnknownException.java} | 22 +-
.../banyandb/v1/client/metadata/Duration.java | 105 ++
.../v1/client/metadata/GroupMetadataRegistry.java | 87 ++
.../metadata/IndexRuleBindingMetadataRegistry.java | 89 ++
.../client/metadata/IndexRuleMetadataRegistry.java | 90 ++
.../client/metadata/MeasureMetadataRegistry.java | 91 ++
.../client/metadata/PropertyMetadataRegistry.java | 91 ++
.../v1/client/metadata/ResourceExist.java} | 20 +-
.../banyandb/v1/client/metadata/Serializable.java} | 31 +-
.../v1/client/metadata/StreamMetadataRegistry.java | 91 ++
.../metadata/TopNAggregationMetadataRegistry.java | 90 ++
.../v1/client/metadata/TraceMetadataRegistry.java | 91 ++
.../banyandb/v1/client/util/CopyOnWriteMap.java | 181 ++++
.../banyandb/v1/client/util/IgnoreHashEquals.java} | 25 +-
.../banyandb/v1/client/util/PrivateKeyUtil.java | 80 ++
.../banyandb/v1/client/util/StatusUtil.java} | 33 +-
.../library/banyandb/v1/client/util/TimeUtils.java | 50 +
.../library-banyandb-client/src/main/proto | 1 +
.../v1/client/AbstractBanyanDBClientTest.java | 380 +++++++
.../banyandb/v1/client/BanyanDBClientTestCI.java | 104 ++
.../banyandb/v1/client/ITBanyanDBAuthTest.java | 110 ++
.../banyandb/v1/client/ITBanyanDBCommonTests.java | 50 +
.../v1/client/ITBanyanDBMeasureQueryTests.java | 183 ++++
.../v1/client/ITBanyanDBPropertyTests.java | 238 +++++
.../v1/client/ITBanyanDBStreamQueryTests.java | 260 +++++
.../v1/client/ITGroupMetadataRegistryTest.java | 203 ++++
.../ITIndexRuleBindingMetadataRegistryTest.java | 119 +++
.../v1/client/ITIndexRuleMetadataRegistryTest.java | 121 +++
.../v1/client/ITMeasureMetadataRegistryTest.java | 179 ++++
.../v1/client/ITPropertyMetadataRegistryTest.java | 118 +++
.../v1/client/ITStreamMetadataRegistryTest.java | 132 +++
.../ITTopNAggregationMetadataRegistryTest.java | 115 ++
.../v1/client/ITTraceMetadataRegistryTest.java | 194 ++++
.../library/banyandb/v1/client/ITTraceTest.java | 331 ++++++
.../banyandb/v1/client/grpc/ExceptionTest.java | 65 ++
.../v1/client/grpc/channel/ChannelManagerTest.java | 145 +++
.../client/grpc/channel/FakeChannelFactory.java} | 30 +-
.../client/grpc/channel/FakeMethodDescriptor.java | 53 +
.../src/test/resources/config.yaml} | 14 +-
oap-server/server-library/pom.xml | 1 +
oap-server/server-starter/pom.xml | 1 -
.../server-starter/src/main/resources/bydb.yml | 2 +
.../storage-banyandb-plugin/pom.xml | 3 +-
.../banyandb/BanyanDBAggregationQueryDAO.java | 12 +-
.../storage/plugin/banyandb/BanyanDBConverter.java | 38 +-
.../plugin/banyandb/BanyanDBIndexInstaller.java | 36 +-
.../plugin/banyandb/BanyanDBNoneStreamDAO.java | 2 +-
.../plugin/banyandb/BanyanDBRecordsQueryDAO.java | 8 +-
.../plugin/banyandb/BanyanDBStorageClient.java | 77 +-
.../plugin/banyandb/BanyanDBStorageConfig.java | 16 +-
.../plugin/banyandb/BanyanDBStorageProvider.java | 2 +-
.../banyandb/BanyanDBUIMenuManagementDAO.java | 2 +-
.../banyandb/BanyanDBUITemplateManagementDAO.java | 2 +-
.../storage/plugin/banyandb/MetadataRegistry.java | 33 +-
.../banyandb/bulk/AbstractBulkWriteProcessor.java | 23 +-
.../banyandb/bulk/MeasureBulkWriteProcessor.java | 21 +-
.../banyandb/bulk/StreamBulkWriteProcessor.java | 21 +-
.../banyandb/bulk/TraceBulkWriteProcessor.java | 21 +-
.../BanyanDBEBPFProfilingScheduleQueryDAO.java | 8 +-
.../measure/BanyanDBHierarchyQueryDAO.java | 8 +-
.../measure/BanyanDBMeasureInsertRequest.java | 2 +-
.../measure/BanyanDBMeasureUpdateRequest.java | 2 +-
.../banyandb/measure/BanyanDBMetadataQueryDAO.java | 10 +-
.../banyandb/measure/BanyanDBMetricsDAO.java | 12 +-
.../banyandb/measure/BanyanDBMetricsQueryDAO.java | 6 +-
.../measure/BanyanDBNetworkAddressAliasDAO.java | 4 +-
.../banyandb/measure/BanyanDBServiceLabelDAO.java | 2 +-
.../measure/BanyanDBTagAutocompleteQueryDAO.java | 8 +-
.../banyandb/measure/BanyanDBTopologyQueryDAO.java | 8 +-
.../banyandb/stream/AbstractBanyanDBDAO.java | 50 +-
.../banyandb/stream/BanyanDBAlarmQueryDAO.java | 8 +-
.../BanyanDBAsyncProfilerTaskLogQueryDAO.java | 6 +-
.../stream/BanyanDBAsyncProfilerTaskQueryDAO.java | 10 +-
.../stream/BanyanDBBrowserLogQueryDAO.java | 6 +-
.../BanyanDBContinuousProfilingPolicyDAO.java | 2 +-
.../stream/BanyanDBEBPFProfilingDataDAO.java | 6 +-
.../stream/BanyanDBEBPFProfilingTaskDAO.java | 8 +-
.../banyandb/stream/BanyanDBEventQueryDAO.java | 12 +-
.../banyandb/stream/BanyanDBJFRDataQueryDAO.java | 6 +-
.../banyandb/stream/BanyanDBLogQueryDAO.java | 8 +-
.../banyandb/stream/BanyanDBPprofDataQueryDAO.java | 6 +-
.../stream/BanyanDBPprofTaskLogQueryDAO.java | 6 +-
.../banyandb/stream/BanyanDBPprofTaskQueryDAO.java | 10 +-
.../stream/BanyanDBProfileTaskLogQueryDAO.java | 6 +-
.../stream/BanyanDBProfileTaskQueryDAO.java | 10 +-
.../BanyanDBProfileThreadSnapshotQueryDAO.java | 10 +-
.../plugin/banyandb/stream/BanyanDBRecordDAO.java | 12 +-
.../stream/BanyanDBSpanAttachedEventQueryDAO.java | 8 +-
.../stream/BanyanDBStreamInsertRequest.java | 2 +-
.../banyandb/trace/BanyanDBTraceInsertRequest.java | 2 +-
.../banyandb/trace/BanyanDBTraceQueryDAO.java | 8 +-
.../banyandb/trace/BanyanDBZipkinQueryDAO.java | 12 +-
.../server/storage/plugin/banyandb/BanyanDBIT.java | 55 +-
.../exporter/test/ProfileSnapshotExporterTest.java | 2 +
.../profile/exporter/test/ProfileTaskQueryDAO.java | 40 +
pom.xml | 5 +
test/e2e-v2/script/env | 2 +-
161 files changed, 9924 insertions(+), 778 deletions(-)
diff --git a/.gitmodules b/.gitmodules
index 8011ff2bf1..5ecb21ea35 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,6 @@
[submodule "skywalking-ui"]
path = skywalking-ui
url = https://github.com/apache/skywalking-booster-ui.git
+[submodule "oap-server/server-library/library-banyandb-client/src/main/proto"]
+ path = oap-server/server-library/library-banyandb-client/src/main/proto
+ url = https://github.com/apache/skywalking-banyandb-client-proto
diff --git a/apm-dist/src/main/assembly/binary.xml b/apm-dist/src/main/assembly/binary.xml
index 0f7a29454d..f6dbf452d3 100644
--- a/apm-dist/src/main/assembly/binary.xml
+++ b/apm-dist/src/main/assembly/binary.xml
@@ -60,7 +60,6 @@
<include>metadata-service-mapping.yaml</include>
<include>trace-sampling-policy-settings.yml</include>
<include>hierarchy-definition.yml</include>
- <include>bydb.dependencies.properties</include>
<include>bydb.yml</include>
<include>bydb-topn.yml</include>
<include>oal/*.oal</include>
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index c40cf1b513..9a496b0c61 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -16,6 +16,9 @@
* Bump up netty to 4.2.9.Final.
* Add support for OpenSearch/ElasticSearch client certificate authentication.
* Fix BanyanDB logs paging query.
+* Replace BanyanDB Java client with native implementation.
+* Remove `bydb.dependencies.properties` and set the compatible BanyanDB API version number in `${SW_STORAGE_BANYANDB_COMPATIBLE_SERVER_API_VERSIONS}`
+* Fix trace profiling query time range condition.
#### UI
* Fix the missing icon in new native trace view.
diff --git a/docs/en/setup/backend/backend-setup.md b/docs/en/setup/backend/backend-setup.md
index 8d95dcd732..38198e03f5 100755
--- a/docs/en/setup/backend/backend-setup.md
+++ b/docs/en/setup/backend/backend-setup.md
@@ -20,8 +20,13 @@ You should set up the database ready before starting the backend. We recommend t
If you want to use other databases, please read the [storage document](backend-storage.md).
Use the docker mode to run BanyanDB containerized.
+The compatible BanyanDB API version number could be found in `/config/bydb.yml`
+```
+${SW_STORAGE_BANYANDB_COMPATIBLE_SERVER_API_VERSIONS}
+```
+The compatible BanyanDB Server version number can get through the [API versions mapping](https://skywalking.apache.org/docs/skywalking-banyandb/latest/installation/versions/).
+
```shell
-# The compatible version number could be found in /config/bydb.dependencies.properties
export BYDB_VERSION=xxx
docker pull apache/skywalking-banyandb:${BYDB_VERSION}
diff --git a/docs/en/setup/backend/configuration-vocabulary.md b/docs/en/setup/backend/configuration-vocabulary.md
index 8e2e55911b..56ec52f16f 100644
--- a/docs/en/setup/backend/configuration-vocabulary.md
+++ b/docs/en/setup/backend/configuration-vocabulary.md
@@ -352,23 +352,24 @@ The following table lists the configuration items:
### Global Configuration
The global settings for the whole BanyanDB:
-| Settings | Value(s) and Explanation | System Environment Variable¹ | Default |
-|---------------------------------|-------------------------------------------------------------------------------------------------------|---------------------------------------------------------|-----------------|
-| targets | Hosts with ports of the BanyanDB. | SW_STORAGE_BANYANDB_TARGETS | 127.0.0.1:17912 |
-| maxBulkSize | The maximum size of write entities in a single batch write call. | SW_STORAGE_BANYANDB_MAX_BULK_SIZE | 10000 |
-| flushInterval | Period of flush interval. In the timeunit of seconds. | SW_STORAGE_BANYANDB_FLUSH_INTERVAL | 15 |
-| flushTimeout | The timeout seconds of a bulk flush. | SW_STORAGE_BANYANDB_FLUSH_TIMEOUT | 10 |
-| concurrentWriteThreads | Concurrent consumer threads for batch writing. | SW_STORAGE_BANYANDB_CONCURRENT_WRITE_THREADS | 15 |
-| profileTaskQueryMaxSize | Max size of ProfileTask to be fetched. | SW_STORAGE_BANYANDB_PROFILE_TASK_QUERY_MAX_SIZE | 200 |
-| resultWindowMaxSize | The maximum size of dataset when the OAP loads cache, such as network aliases. | SW_STORAGE_BANYAND_QUERY_MAX_WINDOW_SIZE | 10000 |
-| metadataQueryMaxSize | The maximum size of metadata per query. | SW_STORAGE_BANYAND_QUERY_MAX_SIZE | 10000 |
-| segmentQueryMaxSize | The maximum size of trace segments per query. | SW_STORAGE_BANYAND_QUERY_SEGMENT_SIZE | 200 |
-| asyncProfilerTaskQueryMaxSize | Max size of AsyncProfilerTask to be fetched. | SW_STORAGE_BANYANDB_ASYNC_PROFILER_TASK_QUERY_MAX_SIZE | 200 |
-| profileDataQueryScrollBatchSize | The batch size of query profiling data. | SW_STORAGE_BANYAND_QUERY_PROFILE_DATA_BATCH_SIZE | 100 |
-| user | The username for BanyanDB authentication. | SW_STORAGE_BANYANDB_USER | - |
-| password | The password for BanyanDB authentication. | SW_STORAGE_BANYANDB_PASSWORD | - |
-| sslTrustCAPath | If the BanyanDB server is configured with TLS, config the TLS cert file path and open tls connection. | SW_STORAGE_BANYANDB_SSL_TRUST_CA_PATH | - |
-| cleanupUnusedTopNRules | Cleanup TopN rules in BanyanDB server that are not configured in the bydb-topn.yml config. | SW_STORAGE_BANYANDB_CLEANUP_UNUSED_TOPN_RULES | true |
+| Settings | Value(s) and Explanation | System Environment Variable¹ | Default |
+|---------------------------------|-------------------------------------------------------------------------------------------------------|--------------------------------------------------------|-------------------|
+| targets | Hosts with ports of the BanyanDB. | SW_STORAGE_BANYANDB_TARGETS | 127.0.0.1:17912 |
+| maxBulkSize | The maximum size of write entities in a single batch write call. | SW_STORAGE_BANYANDB_MAX_BULK_SIZE | 10000 |
+| flushInterval | Period of flush interval. In the timeunit of seconds. | SW_STORAGE_BANYANDB_FLUSH_INTERVAL | 15 |
+| flushTimeout | The timeout seconds of a bulk flush. | SW_STORAGE_BANYANDB_FLUSH_TIMEOUT | 10 |
+| concurrentWriteThreads | Concurrent consumer threads for batch writing. | SW_STORAGE_BANYANDB_CONCURRENT_WRITE_THREADS | 15 |
+| profileTaskQueryMaxSize | Max size of ProfileTask to be fetched. | SW_STORAGE_BANYANDB_PROFILE_TASK_QUERY_MAX_SIZE | 200 |
+| resultWindowMaxSize | The maximum size of dataset when the OAP loads cache, such as network aliases. | SW_STORAGE_BANYAND_QUERY_MAX_WINDOW_SIZE | 10000 |
+| metadataQueryMaxSize | The maximum size of metadata per query. | SW_STORAGE_BANYAND_QUERY_MAX_SIZE | 10000 |
+| segmentQueryMaxSize | The maximum size of trace segments per query. | SW_STORAGE_BANYAND_QUERY_SEGMENT_SIZE | 200 |
+| asyncProfilerTaskQueryMaxSize | Max size of AsyncProfilerTask to be fetched. | SW_STORAGE_BANYANDB_ASYNC_PROFILER_TASK_QUERY_MAX_SIZE | 200 |
+| profileDataQueryScrollBatchSize | The batch size of query profiling data. | SW_STORAGE_BANYAND_QUERY_PROFILE_DATA_BATCH_SIZE | 100 |
+| user | The username for BanyanDB authentication. | SW_STORAGE_BANYANDB_USER | - |
+| password | The password for BanyanDB authentication. | SW_STORAGE_BANYANDB_PASSWORD | - |
+| sslTrustCAPath | If the BanyanDB server is configured with TLS, config the TLS cert file path and open tls connection. | SW_STORAGE_BANYANDB_SSL_TRUST_CA_PATH | - |
+| cleanupUnusedTopNRules | Cleanup TopN rules in BanyanDB server that are not configured in the bydb-topn.yml config. | SW_STORAGE_BANYANDB_CLEANUP_UNUSED_TOPN_RULES | true |
+| namespace | The namespace in BanyanDB to store the data of OAP. | SW_NAMESPACE | default |
### Group Configuration
The settings for each group:
diff --git a/docs/en/setup/backend/storages/banyandb.md b/docs/en/setup/backend/storages/banyandb.md
index a4611b57d7..03eef785e6 100644
--- a/docs/en/setup/backend/storages/banyandb.md
+++ b/docs/en/setup/backend/storages/banyandb.md
@@ -2,18 +2,12 @@
[BanyanDB](https://github.com/apache/skywalking-banyandb) is a dedicated storage implementation developed by the SkyWalking Team and the community. Activate BanyanDB as the storage by setting the storage provider to **banyandb**.
-The BanyanDB server compatibility relies on API and release versions, which are defined in `bydb.dependencies.properties`
-```shell
-# BanyanDB version is the version number of BanyanDB Server release.
-# This is the bundled and tested BanyanDB release version
-bydb.version=x.y
-# BanyanDB API version is the version number of the BanyanDB query APIs
-# OAP server has bundled implementation of BanyanDB Java client.
-# Please check BanyanDB documentation for the API version compatibility.
-# https://skywalking.apache.org/docs/skywalking-banyandb/next/installation/versions
-# Each `bydb.api.version` could have multiple compatible release version(`bydb.version`).
-bydb.api.version=x.y
+The BanyanDB server compatibility relies on API and release versions,
+The compatible BanyanDB API version number could be found in `/config/bydb.yml`
+```
+${SW_STORAGE_BANYANDB_COMPATIBLE_SERVER_API_VERSIONS}
```
+The compatible BanyanDB Server version number can get through the [API versions mapping](https://skywalking.apache.org/docs/skywalking-banyandb/latest/installation/versions/).
If the BanyanDB server API version is not compatible with the OAP server, the OAP server will not start, and the following error message will be displayed:
```shell
@@ -68,6 +62,11 @@ global:
sslTrustCAPath: ${SW_STORAGE_BANYANDB_SSL_TRUST_CA_PATH:""}
# Cleanup TopN rules in BanyanDB server that are not configured in the bydb-topn.yml config.
cleanupUnusedTopNRules: ${SW_STORAGE_BANYANDB_CLEANUP_UNUSED_TOPN_RULES:true}
+ # The namespace in BanyanDB to store the data of OAP, if not set, the default is "sw".
+ # OAP will create BanyanDB Groups using the format of "{namespace}_{group name}", such as "sw_records".
+ namespace: ${SW_NAMESPACE:"sw"}
+ # The compatible server API versions of BanyanDB.
+ compatibleServerApiVersions: ${SW_STORAGE_BANYANDB_COMPATIBLE_SERVER_API_VERSIONS:"0.9"}
groups:
# The group settings of record.
@@ -365,7 +364,6 @@ BanyanDB Server supports two installation modes:
Use the docker mode to run BanyanDB containerized.
```shell
-# The compatible version number could be found in /config/bydb.dependencies.properties
export BYDB_VERSION=xxx
docker pull apache/skywalking-banyandb:${BYDB_VERSION}
@@ -393,4 +391,4 @@ docker run -d \
- **Cluster Mode**: Suitable for large-scale deployments.
- **Configuration**: `targets` is the IP address/hostname and port of the `liaison` nodes, separated by commas. `Liaison` nodes are the entry points of the BanyanDB cluster.
-For more details, refer to the documentation of [BanyanDB](https://skywalking.apache.org/docs/skywalking-banyandb/latest/readme/) and the [BanyanDB Java Client](https://github.com/apache/skywalking-banyandb-java-client) subprojects.
+For more details, refer to the documentation of [BanyanDB](https://skywalking.apache.org/docs/skywalking-banyandb/latest/readme/).
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/profiling/trace/ProfileTaskQueryService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/profiling/trace/ProfileTaskQueryService.java
index a3db0ebc48..f62b55ea69 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/profiling/trace/ProfileTaskQueryService.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/profiling/trace/ProfileTaskQueryService.java
@@ -191,17 +191,12 @@ public class ProfileTaskQueryService implements Service {
public List<SegmentRecord> getTaskSegments(String taskId) throws IOException {
List<ProfileThreadSnapshotRecord> records = getProfileThreadSnapshotQueryDAO().queryRecords(taskId);
List<String> profiledSegmentIdList = records.stream().map(ProfileThreadSnapshotRecord::getSegmentId).collect(Collectors.toList());
- long endTimestamp = 0;
- for (ProfileThreadSnapshotRecord record : records) {
- if (record.getDumpTime() > endTimestamp) {
- endTimestamp = record.getDumpTime();
- }
- }
- long startTimestamp = endTimestamp - 5 * 60 * 1000; // 5 minutes
+ ProfileTask profileTask = getProfileTaskDAO().getById(taskId);
+ long endTimestamp = profileTask.getStartTime() + (long) profileTask.getDuration() * 60 * 1000; // duration minute to ms
Duration duration = new Duration();
duration.setStep(Step.SECOND);
- DateTime endTime = new DateTime(endTimestamp);
- DateTime startTime = new DateTime(startTimestamp);
+ DateTime endTime = new DateTime(endTimestamp + 15 * 60 * 1000); // 15 minutes later to cover delay
+ DateTime startTime = new DateTime(endTimestamp - 10 * 60 * 1000); // 10 minutes before end time
duration.setStart(startTime.toString("yyyy-MM-dd HHmmss"));
duration.setEnd(endTime.toString("yyyy-MM-dd HHmmss"));
return getTraceQueryDAO().queryBySegmentIdList(profiledSegmentIdList, duration);
diff --git a/oap-server/server-library/library-banyandb-client/pom.xml b/oap-server/server-library/library-banyandb-client/pom.xml
new file mode 100644
index 0000000000..933e9ee4e7
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/pom.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You 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.
+ ~
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>server-library</artifactId>
+ <groupId>org.apache.skywalking</groupId>
+ <version>${revision}</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>library-banyandb-client</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-netty</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-handler</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-stub</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-protobuf</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>build.buf.protoc-gen-validate</groupId>
+ <artifactId>protoc-gen-validate</artifactId>
+ <version>${bufbuild.protoc-gen-validate.version}</version>
+ <type>pom</type>
+ </dependency>
+ <dependency>
+ <groupId>build.buf.protoc-gen-validate</groupId>
+ <artifactId>pgv-java-stub</artifactId>
+ <version>${bufbuild.protoc-gen-validate.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-testing</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>server-testing</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>library-integration-test</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>kr.motd.maven</groupId>
+ <artifactId>os-maven-plugin</artifactId>
+ <version>${os-maven-plugin.version}</version>
+ <executions>
+ <execution>
+ <phase>initialize</phase>
+ <goals>
+ <goal>detect</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.xolstice.maven.plugins</groupId>
+ <artifactId>protobuf-maven-plugin</artifactId>
+ <version>${protobuf-maven-plugin.version}</version>
+ <configuration>
+ <!--
+ The version of protoc must match protobuf-java. If you don't depend on
+ protobuf-java directly, you will be transitively depending on the
+ protobuf-java version that grpc depends on.
+ -->
+ <protocArtifact>
+ com.google.protobuf:protoc:${com.google.protobuf.protoc.version}:exe:${os.detected.classifier}
+ </protocArtifact>
+ <pluginId>grpc-java</pluginId>
+ <pluginArtifact>
+ io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc-java.plugin.version}:exe:${os.detected.classifier}
+ </pluginArtifact>
+ <protoSourceRoot>${project.basedir}/src/main/proto/proto</protoSourceRoot>
+ <includes>
+ <include>**/*.proto</include>
+ </includes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>grpc-build</id>
+ <goals>
+ <goal>compile</goal>
+ <goal>compile-custom</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>protoc-java-pgv</id>
+ <goals>
+ <goal>compile-custom</goal>
+ </goals>
+ <configuration>
+ <pluginParameter>lang=java</pluginParameter>
+ <pluginId>java-pgv</pluginId>
+ <pluginArtifact>
+ build.buf.protoc-gen-validate:protoc-gen-validate:${bufbuild.protoc-gen-validate.version}:exe:${os.detected.classifier}
+ </pluginArtifact>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractCriteria.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractCriteria.java
index dbef0e0068..ad044c20f4 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractCriteria.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
+public abstract class AbstractCriteria {
+ public abstract BanyandbModel.Criteria build();
+}
- @Override
- public void onInsertCompleted() {
+abstract class LogicalExpression extends AbstractCriteria {
+ abstract AbstractCriteria left();
- }
-}
\ No newline at end of file
+ abstract AbstractCriteria right();
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractQuery.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractQuery.java
new file mode 100644
index 0000000000..601388d01b
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractQuery.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.InvalidReferenceException;
+
+public abstract class AbstractQuery<T> {
+ /**
+ * Group of the current entity
+ */
+ protected final List<String> groups;
+ /**
+ * Owner name of the current entity
+ */
+ protected final String name;
+ /**
+ * The time range for query.
+ */
+ protected final TimestampRange timestampRange;
+ /**
+ * Query conditions.
+ */
+ protected final List<LogicalExpression> conditions;
+
+ /**
+ * The projections of query result.
+ * These should have defined in the schema.
+ */
+ protected final Map<String/*tagName*/, String/*tagFamilyName*/> tagProjections;
+
+ /**
+ * Query criteria.
+ */
+ protected AbstractCriteria criteria;
+
+ /**
+ * Enable or disable trace.
+ */
+ protected boolean trace;
+
+ public AbstractQuery(List<String> groups,
+ String name,
+ TimestampRange timestampRange,
+ Map<String/*tagName*/, String/*tagFamilyName*/> tagProjections) {
+ this.groups = groups;
+ this.name = name;
+ this.timestampRange = timestampRange;
+ this.conditions = new ArrayList<>(10);
+ this.tagProjections = tagProjections;
+ }
+
+ /**
+ * Constructor with tag projections as a set of tag names which have no specified tag family.
+ * Such as Property/Trace query
+ */
+ public AbstractQuery(List<String> groups,
+ String name,
+ TimestampRange timestampRange,
+ Set<String> tagProjections) {
+ this.groups = groups;
+ this.name = name;
+ this.timestampRange = timestampRange;
+ this.conditions = new ArrayList<>(10);
+ this.tagProjections = new HashMap<>();
+ tagProjections.forEach(tagName -> this.tagProjections.put(tagName, null));
+ }
+
+ /**
+ * Fluent API for appending a and
+ *
+ * @param condition the query condition to be appended
+ */
+ public AbstractQuery<T> and(PairQueryCondition<?> condition) {
+ this.conditions.add(new LogicalExpression(BanyandbModel.LogicalExpression.LogicalOp.LOGICAL_OP_AND, condition));
+ return this;
+ }
+
+ /**
+ * Fluent API for appending or
+ *
+ * @param condition the query condition to be appended
+ */
+ public AbstractQuery<T> or(PairQueryCondition<?> condition) {
+ this.conditions.add(new LogicalExpression(BanyandbModel.LogicalExpression.LogicalOp.LOGICAL_OP_OR, condition));
+ return this;
+ }
+
+ /**
+ * Fluent API for appending query criteria
+ *
+ * @param criteria the query criteria to be appended
+ */
+ public AbstractQuery<T> criteria(AbstractCriteria criteria) {
+ this.criteria = criteria;
+ return this;
+ }
+
+ /**
+ * Enable trace for the query.
+ */
+ public AbstractQuery<T> enableTrace() {
+ this.trace = true;
+ return this;
+ }
+
+ /**
+ * @return QueryRequest for gRPC level query.
+ * @throws BanyanDBException thrown from entity build, e.g. invalid reference to non-exist fields or tags.
+ */
+ abstract T build() throws BanyanDBException;
+
+ protected Optional<BanyandbModel.Criteria> buildCriteria() {
+ if (criteria != null) {
+ return Optional.of(criteria.build());
+ }
+ if (conditions.isEmpty()) {
+ return Optional.empty();
+ }
+ if (conditions.size() == 1) {
+ return Optional.of(conditions.get(0).getCond().build());
+ }
+ return Optional.of(conditions.stream()
+ .reduce(null, (criteria, logicalExpression) -> {
+ BanyandbModel.LogicalExpression.Builder b = BanyandbModel.LogicalExpression.newBuilder();
+ if (criteria != null) {
+ b.setRight(criteria);
+ }
+ return BanyandbModel.Criteria.newBuilder()
+ .setLe(b.setOp(logicalExpression.getOp())
+ .setLeft(logicalExpression.getCond().build())).build();
+ }, (first, second) -> second));
+ }
+
+ protected BanyandbModel.TagProjection buildTagProjections() throws BanyanDBException {
+ return this.buildTagProjections(this.tagProjections.keySet());
+ }
+
+ protected BanyandbModel.TagProjection buildTagProjections(Iterable<String> tagProjections) throws BanyanDBException {
+ final ListMultimap<String, String> projectionMap = ArrayListMultimap.create();
+ for (final String tagName : tagProjections) {
+ final String tagFamily = this.tagProjections.get(tagName);
+ if (Strings.isNullOrEmpty(tagFamily)) {
+ throw InvalidReferenceException.fromInvalidTag(tagName);
+ }
+ projectionMap.put(tagFamily, tagName);
+ }
+
+ final BanyandbModel.TagProjection.Builder b = BanyandbModel.TagProjection.newBuilder();
+ for (final String tagFamilyName : projectionMap.keySet()) {
+ b.addTagFamilies(BanyandbModel.TagProjection.TagFamily.newBuilder()
+ .setName(tagFamilyName)
+ .addAllTags(projectionMap.get(tagFamilyName))
+ .build());
+ }
+ return b.build();
+ }
+
+ public static class OrderBy {
+ /**
+ * The field name for ordering.
+ */
+ private final String indexRuleName;
+ /**
+ * The type of ordering.
+ */
+ private final Sort type;
+
+ /**
+ * Create an orderBy condition with given rule name and sort type
+ */
+ public OrderBy(final String indexRuleName, final Sort type) {
+ this.indexRuleName = indexRuleName;
+ this.type = type;
+ }
+
+ /**
+ * Create an orderBy condition with timestamp and sort type
+ */
+ public OrderBy(final Sort type) {
+ this.indexRuleName = null;
+ this.type = type;
+ }
+
+ BanyandbModel.QueryOrder build() {
+ final BanyandbModel.QueryOrder.Builder builder = BanyandbModel.QueryOrder.newBuilder();
+ if (indexRuleName != null) {
+ builder.setIndexRuleName(indexRuleName);
+ }
+ builder.setSort(
+ Sort.DESC.equals(type) ? BanyandbModel.Sort.SORT_DESC : BanyandbModel.Sort.SORT_ASC);
+ return builder.build();
+ }
+ }
+
+ @RequiredArgsConstructor
+ @Getter(AccessLevel.PROTECTED)
+ public enum Sort {
+ UNSPECIFIED, ASC, DESC;
+ }
+
+ @AllArgsConstructor
+ @Getter
+ static class LogicalExpression {
+ private final BanyandbModel.LogicalExpression.LogicalOp op;
+
+ private final PairQueryCondition<?> cond;
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractWrite.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractWrite.java
new file mode 100644
index 0000000000..413545d9b5
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/AbstractWrite.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.Optional;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+
+public abstract class AbstractWrite<P extends com.google.protobuf.GeneratedMessageV3> {
+ /**
+ * Timestamp represents the time of the current data point, in milliseconds.
+ * <p>
+ * <b>When to set:</b>
+ * <ul>
+ * <li><b>Stream and Measure writes:</b> This field <i>must</i> be set to indicate the event time.</li>
+ * <li><b>Trace writes:</b> This field is <i>not needed</i> and should be left unset; trace data does not require an explicit timestamp here.</li>
+ * </ul>
+ */
+ @Getter
+ protected Optional<Long> timestamp;
+
+ @Getter
+ protected final BanyandbCommon.Metadata entityMetadata;
+
+ public AbstractWrite(BanyandbCommon.Metadata entityMetadata, long timestamp) {
+ this(entityMetadata);
+ this.timestamp = Optional.of(timestamp);
+ }
+
+ /**
+ * Build a write request without initial timestamp.
+ */
+ AbstractWrite(BanyandbCommon.Metadata entityMetadata) {
+ if (entityMetadata == null) {
+ throw new IllegalArgumentException("metadata not found");
+ }
+ this.entityMetadata = entityMetadata;
+ }
+
+ public P build() {
+ return build(entityMetadata);
+ }
+
+ public P buildOnlyValues() {
+ return buildValues();
+ }
+
+ protected abstract P build(BanyandbCommon.Metadata metadata);
+
+ protected abstract P buildValues();
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/And.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/And.java
new file mode 100644
index 0000000000..fba9ac837c
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/And.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import lombok.Builder;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+
+@Builder
+@Getter
+public class And extends LogicalExpression {
+ private final AbstractCriteria left;
+ private final AbstractCriteria right;
+
+ @Override
+ public BanyandbModel.Criteria build() {
+ return BanyandbModel.Criteria.newBuilder()
+ .setLe(BanyandbModel.LogicalExpression.newBuilder()
+ .setLeft(left.build())
+ .setRight(right.build())
+ .setOp(BanyandbModel.LogicalExpression.LogicalOp.LOGICAL_OP_AND)
+ .build())
+ .build();
+ }
+
+ public static And create(AbstractCriteria left, AbstractCriteria right) {
+ return And.builder().left(left).right(right).build();
+ }
+
+ @Override
+ AbstractCriteria left() {
+ return left;
+ }
+
+ @Override
+ AbstractCriteria right() {
+ return right;
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/BanyanDBClient.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/BanyanDBClient.java
new file mode 100644
index 0000000000..0d0f82e77f
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/BanyanDBClient.java
@@ -0,0 +1,1114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.protobuf.Timestamp;
+import io.grpc.Channel;
+import io.grpc.ClientInterceptors;
+import io.grpc.ManagedChannel;
+import io.grpc.Status;
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.URI;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Group;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Metadata;
+import org.apache.skywalking.banyandb.common.v1.ServiceGrpc;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.IndexRule;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.IndexRuleBinding;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Measure;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Stream;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Subject;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TopNAggregation;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Trace;
+import org.apache.skywalking.banyandb.measure.v1.BanyandbMeasure;
+import org.apache.skywalking.banyandb.measure.v1.MeasureServiceGrpc;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty;
+import org.apache.skywalking.banyandb.stream.v1.BanyandbStream;
+import org.apache.skywalking.banyandb.stream.v1.StreamServiceGrpc;
+import org.apache.skywalking.banyandb.trace.v1.BanyandbTrace;
+import org.apache.skywalking.banyandb.trace.v1.TraceServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.auth.AuthInterceptor;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.HandleExceptionsWith;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.channel.ChannelManager;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.channel.DefaultChannelFactory;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.GroupMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.IndexRuleBindingMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.IndexRuleMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.MeasureMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.PropertyMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.ResourceExist;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.StreamMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.TopNAggregationMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.TraceMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.util.TimeUtils;
+import org.apache.skywalking.oap.server.library.util.StringUtil;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+/**
+ * BanyanDBClient represents a client instance interacting with BanyanDB server.
+ * This is built on the top of BanyanDB v1 gRPC APIs.
+ *
+ * <pre>{@code
+ * // use `default` group
+ * client = new BanyanDBClient("127.0.0.1", 17912);
+ * // to send any request, a connection to the server must be estabilished
+ * client.connect();
+ * }</pre>
+ */
+@Slf4j
+public class BanyanDBClient implements Closeable {
+ public static final ZonedDateTime DEFAULT_EXPIRE_AT = ZonedDateTime.of(2099, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC);
+ private final String[] targets;
+ /**
+ * Options for server connection.
+ */
+ @Getter
+ private final Options options;
+ /**
+ * gRPC connection.
+ */
+ @Getter
+ private volatile Channel channel;
+ /**
+ * gRPC client stub
+ */
+ @Getter
+ private StreamServiceGrpc.StreamServiceStub streamServiceStub;
+ /**
+ * gRPC client stub
+ */
+ @Getter
+ private MeasureServiceGrpc.MeasureServiceStub measureServiceStub;
+ /**
+ * gRPC client stub
+ */
+ @Getter
+ private TraceServiceGrpc.TraceServiceStub traceServiceStub;
+ /**
+ * gRPC future stub.
+ */
+ @Getter
+ private StreamServiceGrpc.StreamServiceBlockingStub streamServiceBlockingStub;
+ /**
+ * gRPC future stub.
+ */
+ @Getter
+ private MeasureServiceGrpc.MeasureServiceBlockingStub measureServiceBlockingStub;
+ /**
+ * gRPC future stub.
+ */
+ @Getter
+ private TraceServiceGrpc.TraceServiceBlockingStub traceServiceBlockingStub;
+ /**
+ * The connection status.
+ */
+ private volatile boolean isConnected = false;
+ /**
+ * A lock to control the race condition in establishing and disconnecting network connection.
+ */
+ private final ReentrantLock connectionEstablishLock;
+
+ /**
+ * Create a BanyanDB client instance with a default options.
+ *
+ * @param targets server targets
+ */
+ public BanyanDBClient(String... targets) {
+ this(targets, new Options());
+ }
+
+ /**
+ * Create a BanyanDB client instance with a customized options.
+ *
+ * @param targets server targets
+ * @param options customized options
+ */
+ public BanyanDBClient(String[] targets, Options options) {
+ String[] tt = Preconditions.checkNotNull(targets, "targets");
+ checkState(tt.length > 0, "targets' size must be more than 1");
+ tt = Arrays.stream(tt).filter(t -> !Strings.isNullOrEmpty(t)).toArray(size -> new String[size]);
+ checkState(tt.length > 0, "valid targets' size must be more than 1");
+ this.targets = tt;
+ this.options = options;
+ this.connectionEstablishLock = new ReentrantLock();
+ }
+
+ /**
+ * Construct a connection to the server.
+ *
+ * @throws IOException thrown if fail to create a connection
+ */
+ public void connect() throws IOException {
+ connectionEstablishLock.lock();
+ try {
+ if (!isConnected) {
+ URI[] addresses = new URI[this.targets.length];
+ for (int i = 0; i < this.targets.length; i++) {
+ addresses[i] = URI.create("//" + this.targets[i]);
+ }
+ Channel rawChannel = ChannelManager.create(this.options.buildChannelManagerSettings(),
+ new DefaultChannelFactory(addresses, this.options));
+ Channel interceptedChannel = rawChannel;
+ // register auth interceptor
+ String username = options.getUsername();
+ String password = options.getPassword();
+ if (StringUtil.isNotBlank(username) && StringUtil.isNotBlank(password)) {
+ interceptedChannel = ClientInterceptors.intercept(rawChannel,
+ new AuthInterceptor(username, password));
+ }
+ // Ensure this.channel is assigned only once.
+ this.channel = interceptedChannel;
+ streamServiceBlockingStub = StreamServiceGrpc.newBlockingStub(this.channel);
+ measureServiceBlockingStub = MeasureServiceGrpc.newBlockingStub(this.channel);
+ traceServiceBlockingStub = TraceServiceGrpc.newBlockingStub(this.channel);
+ streamServiceStub = StreamServiceGrpc.newStub(this.channel);
+ measureServiceStub = MeasureServiceGrpc.newStub(this.channel);
+ traceServiceStub = TraceServiceGrpc.newStub(this.channel);
+ isConnected = true;
+ }
+ } finally {
+ connectionEstablishLock.unlock();
+ }
+ }
+
+ @VisibleForTesting
+ void connect(Channel channel) {
+ connectionEstablishLock.lock();
+ try {
+ if (!isConnected) {
+ this.channel = channel;
+ streamServiceBlockingStub = StreamServiceGrpc.newBlockingStub(this.channel);
+ measureServiceBlockingStub = MeasureServiceGrpc.newBlockingStub(this.channel);
+ traceServiceBlockingStub = TraceServiceGrpc.newBlockingStub(this.channel);
+ streamServiceStub = StreamServiceGrpc.newStub(this.channel);
+ measureServiceStub = MeasureServiceGrpc.newStub(this.channel);
+ traceServiceStub = TraceServiceGrpc.newStub(this.channel);
+ isConnected = true;
+ }
+ } finally {
+ connectionEstablishLock.unlock();
+ }
+ }
+
+ /**
+ * Build a MeasureWrite request.
+ *
+ * @param group the group of the measure
+ * @param name the name of the measure
+ * @param timestamp the timestamp of the measure
+ * @return the request to be built
+ */
+ public MeasureWrite createMeasureWrite(String group, String name, long timestamp) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ return new MeasureWrite(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build(), timestamp);
+ }
+
+ /**
+ * Build a StreamWrite request.
+ *
+ * @param group the group of the stream
+ * @param name the name of the stream
+ * @param elementId the primary key of the stream
+ * @return the request to be built
+ */
+ public StreamWrite createStreamWrite(String group, String name, final String elementId) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ return new StreamWrite(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build(), elementId);
+ }
+
+ /**
+ * Build a TraceWrite request without initial timestamp.
+ *
+ * @param group the group of the trace
+ * @param name the name of the trace
+ * @return the request to be built
+ */
+ public TraceWrite createTraceWrite(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ return new TraceWrite(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build());
+ }
+
+ /**
+ * Query streams according to given conditions
+ *
+ * @param streamQuery condition for query
+ * @return hint streams.
+ */
+ public StreamQueryResponse query(StreamQuery streamQuery) throws BanyanDBException {
+ checkState(this.streamServiceStub != null, "stream service is null");
+
+ for (String group : streamQuery.groups) {
+ final BanyandbStream.QueryResponse response =
+ HandleExceptionsWith.callAndTranslateApiException(() ->
+ this.streamServiceBlockingStub
+ .withDeadlineAfter(
+ this.getOptions().getDeadline(),
+ TimeUnit.SECONDS
+ )
+ .query(streamQuery.build()));
+ return new StreamQueryResponse(response);
+ }
+ throw new RuntimeException("No metadata found for the query");
+ }
+
+ /**
+ * Query TopN according to given conditions
+ *
+ * @param topNQuery condition for query
+ * @return hint topN.
+ */
+ public TopNQueryResponse query(TopNQuery topNQuery) throws BanyanDBException {
+ checkState(this.measureServiceStub != null, "measure service is null");
+
+ final BanyandbMeasure.TopNResponse response = HandleExceptionsWith.callAndTranslateApiException(() ->
+ this.measureServiceBlockingStub
+ .withDeadlineAfter(this.getOptions().getDeadline(), TimeUnit.SECONDS)
+ .topN(topNQuery.build()));
+ return new TopNQueryResponse(response);
+ }
+
+ /**
+ * Query measures according to given conditions
+ *
+ * @param measureQuery condition for query
+ * @return hint measures.
+ */
+ public MeasureQueryResponse query(MeasureQuery measureQuery) throws BanyanDBException {
+ checkState(this.streamServiceStub != null, "measure service is null");
+ for (String group : measureQuery.groups) {
+ final BanyandbMeasure.QueryResponse response =
+ HandleExceptionsWith.callAndTranslateApiException(() ->
+ this.measureServiceBlockingStub
+ .withDeadlineAfter(
+ this.getOptions()
+ .getDeadline(),
+ TimeUnit.SECONDS
+ )
+ .query(
+ measureQuery.build()));
+ return new MeasureQueryResponse(response);
+ }
+ throw new RuntimeException("No metadata found for the query");
+ }
+
+ /**
+ * Query traces according to given conditions
+ *
+ * @param traceQuery condition for query
+ * @return trace query response.
+ */
+ public TraceQueryResponse query(TraceQuery traceQuery) throws BanyanDBException {
+ checkState(this.traceServiceStub != null, "trace service is null");
+
+ for (String group : traceQuery.groups) {
+ final BanyandbTrace.QueryResponse response =
+ HandleExceptionsWith.callAndTranslateApiException(() ->
+ this.traceServiceBlockingStub
+ .withDeadlineAfter(
+ this.getOptions().getDeadline(),
+ TimeUnit.SECONDS
+ )
+ .query(traceQuery.build()));
+ return new TraceQueryResponse(response);
+
+ }
+ throw new RuntimeException("No metadata found for the query");
+ }
+
+ /**
+ * Define a new group and attach to the current client.
+ *
+ * @param group the group to be created
+ * @return a grouped client
+ */
+ public Group define(Group group) throws BanyanDBException {
+ GroupMetadataRegistry registry = new GroupMetadataRegistry(checkNotNull(this.channel));
+ registry.create(group);
+ return registry.get(null, group.getMetadata().getName());
+ }
+
+ /**
+ * Define a new stream
+ *
+ * @param stream the stream to be created
+ */
+ public void define(Stream stream) throws BanyanDBException {
+ StreamMetadataRegistry streamRegistry = new StreamMetadataRegistry(checkNotNull(this.channel));
+ long modRevision = streamRegistry.create(stream);
+ stream = stream.toBuilder().setMetadata(stream.getMetadata().toBuilder().setModRevision(modRevision)).build();
+ }
+
+ /**
+ * Define a new stream with index rules,
+ * @param stream the stream to be created
+ * @param indexRules the index rules to be created
+ */
+ public void define(Stream stream, List<IndexRule> indexRules) throws BanyanDBException {
+ define(stream);
+ defineIndexRules(stream, indexRules);
+ }
+
+ /**
+ * Define a new measure
+ *
+ * @param measure the measure to be created
+ */
+ public void define(Measure measure) throws BanyanDBException {
+ MeasureMetadataRegistry measureRegistry = new MeasureMetadataRegistry(checkNotNull(this.channel));
+ long modRevision = measureRegistry.create(measure);
+ measure = measure.toBuilder().setMetadata(measure.getMetadata().toBuilder().setModRevision(modRevision)).build();
+ }
+
+ /**
+ * Define a new measure with index rules
+ * @param measure the measure to be created
+ * @param indexRules the index rules to be created
+ */
+ public void define(Measure measure, List<IndexRule> indexRules) throws BanyanDBException {
+ define(measure);
+ defineIndexRules(measure, indexRules);
+ }
+
+ /**
+ * Define a new TopNAggregation
+ *
+ * @param topNAggregation the topN rule to be created
+ */
+ public void define(TopNAggregation topNAggregation) throws BanyanDBException {
+ TopNAggregationMetadataRegistry registry = new TopNAggregationMetadataRegistry(checkNotNull(this.channel));
+ registry.create(topNAggregation);
+ }
+
+ /**
+ * Define a new IndexRule
+ * @param indexRule the index rule to be created
+ */
+ public void define(IndexRule indexRule) throws BanyanDBException {
+ IndexRuleMetadataRegistry registry = new IndexRuleMetadataRegistry(checkNotNull(this.channel));
+ registry.create(indexRule);
+ }
+
+ /**
+ * Define a new IndexRuleBinding, if the beginAt and expireAt are not set, the default value will be used.
+ * The default value of beginAt is the current time, and the default value of expireAt is 2099-01-01 00:00:00 UTC.
+ * @param indexRuleBinding the index rule binding to be created
+ */
+ public void define(IndexRuleBinding indexRuleBinding) throws BanyanDBException {
+ ZonedDateTime beginAt = indexRuleBinding.getBeginAt() == Timestamp.getDefaultInstance() ? ZonedDateTime.now() : TimeUtils.parseTimestamp(indexRuleBinding.getBeginAt());
+ ZonedDateTime expireAt = indexRuleBinding.getExpireAt() == Timestamp.getDefaultInstance() ? DEFAULT_EXPIRE_AT : TimeUtils.parseTimestamp(indexRuleBinding.getExpireAt());
+ this.define(indexRuleBinding, beginAt, expireAt);
+ }
+
+ /**
+ * Define a new IndexRuleBinding
+ * @param indexRuleBinding the index rule binding to be created
+ * @param beginAt the beginning time of the index rule binding
+ * @param expireAt the expiry time of the index rule binding
+ */
+ public void define(IndexRuleBinding indexRuleBinding, ZonedDateTime beginAt, ZonedDateTime expireAt) throws BanyanDBException {
+ IndexRuleBindingMetadataRegistry registry = new IndexRuleBindingMetadataRegistry(checkNotNull(this.channel));
+ indexRuleBinding = indexRuleBinding.toBuilder()
+ .setBeginAt(TimeUtils.buildTimestamp(beginAt))
+ .setExpireAt(TimeUtils.buildTimestamp(expireAt))
+ .build();
+ registry.create(indexRuleBinding);
+ }
+
+ /**
+ * Bind index rule to the stream
+ * By default, the index rule binding will be active from now, and it will never be expired.
+ * @param stream the subject of index rule binding
+ * @param indexRules rules to be bounded
+ */
+ public void defineIndexRules(Stream stream, List<IndexRule> indexRules) throws BanyanDBException {
+ Preconditions.checkArgument(stream != null, "stream cannot be null");
+
+ IndexRuleMetadataRegistry irRegistry = new IndexRuleMetadataRegistry(checkNotNull(this.channel));
+ for (final IndexRule ir : indexRules) {
+ try {
+ irRegistry.create(ir);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.ALREADY_EXISTS)) {
+ continue;
+ }
+ throw ex;
+ }
+ }
+ if (indexRules.isEmpty()) {
+ return;
+ }
+
+ List<String> indexRuleNames = indexRules.stream()
+ .map(indexRule -> indexRule.getMetadata().getName())
+ .collect(Collectors.toList());
+
+ IndexRuleBinding binding = IndexRuleBinding.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup(
+ stream.getMetadata().getGroup())
+ .setName(
+ stream.getMetadata().getName()))
+ .setSubject(Subject.newBuilder()
+ .setName(stream.getMetadata()
+ .getName())
+ .setCatalog(
+ BanyandbCommon.Catalog.CATALOG_STREAM))
+ .addAllRules(indexRuleNames).build();
+ this.define(binding);
+ }
+
+ /**
+ * Bind index rule to the measure.
+ * By default, the index rule binding will be active from now, and it will never be expired.
+ *
+ * @param measure the subject of index rule binding
+ * @param indexRules rules to be bounded
+ */
+ public void defineIndexRules(Measure measure, List<IndexRule> indexRules) throws BanyanDBException {
+ Preconditions.checkArgument(measure != null, "measure cannot be null");
+
+ IndexRuleMetadataRegistry irRegistry = new IndexRuleMetadataRegistry(checkNotNull(this.channel));
+ for (final IndexRule ir : indexRules) {
+ try {
+ irRegistry.create(ir);
+ } catch (BanyanDBException ex) {
+ // multiple entity can share a single index rule
+ if (ex.getStatus().equals(Status.Code.ALREADY_EXISTS)) {
+ continue;
+ }
+ throw ex;
+ }
+ }
+ if (indexRules.isEmpty()) {
+ return;
+ }
+
+ List<String> indexRuleNames = indexRules.stream().map(indexRule -> indexRule.getMetadata().getName()).collect(Collectors.toList());
+
+ IndexRuleBinding binding = IndexRuleBinding.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup(
+ measure.getMetadata().getGroup())
+ .setName(
+ measure.getMetadata().getName()))
+ .setSubject(Subject.newBuilder()
+ .setName(measure.getMetadata()
+ .getName())
+ .setCatalog(
+ BanyandbCommon.Catalog.CATALOG_MEASURE))
+ .addAllRules(indexRuleNames).build();
+ this.define(binding);
+ }
+
+ /**
+ * Update the group
+ *
+ * @param group the group to be updated
+ */
+ public void update(Group group) throws BanyanDBException {
+ GroupMetadataRegistry registry = new GroupMetadataRegistry(checkNotNull(this.channel));
+ registry.update(group);
+ }
+
+ /**
+ * Update the stream
+ * @param stream the stream to be updated
+ */
+ public void update(Stream stream) throws BanyanDBException {
+ StreamMetadataRegistry streamRegistry = new StreamMetadataRegistry(checkNotNull(this.channel));
+ streamRegistry.update(stream);
+ }
+
+ /**
+ * Update the measure
+ *
+ * @param measure the measure to be updated
+ */
+ public void update(Measure measure) throws BanyanDBException {
+ MeasureMetadataRegistry measureRegistry = new MeasureMetadataRegistry(checkNotNull(this.channel));
+ measureRegistry.update(measure);
+ }
+
+ /**
+ * Update the TopNAggregation
+ * @param topNAggregation the topN rule to be updated
+ */
+ public void update(TopNAggregation topNAggregation) throws BanyanDBException {
+ TopNAggregationMetadataRegistry registry = new TopNAggregationMetadataRegistry(checkNotNull(this.channel));
+ registry.update(topNAggregation);
+ }
+
+ /**
+ * Update the IndexRule
+ * @param indexRule the index rule to be updated
+ */
+ public void update(IndexRule indexRule) throws BanyanDBException {
+ IndexRuleMetadataRegistry registry = new IndexRuleMetadataRegistry(checkNotNull(this.channel));
+ registry.update(indexRule);
+ }
+
+ /**
+ * Update the IndexRuleBinding
+ * @param indexRuleBinding the index rule binding to be updated
+ */
+ public void update(IndexRuleBinding indexRuleBinding) throws BanyanDBException {
+ IndexRuleBindingMetadataRegistry registry = new IndexRuleBindingMetadataRegistry(checkNotNull(this.channel));
+ registry.update(indexRuleBinding);
+ }
+
+ /**
+ * Delete the group
+ * @param name name of the group
+ * @return true if the group is deleted successfully
+ */
+ public boolean deleteGroup(String name) throws BanyanDBException {
+ GroupMetadataRegistry registry = new GroupMetadataRegistry(checkNotNull(this.channel));
+ return registry.delete(name, name);
+ }
+
+ /**
+ * Delete a stream
+ * @param group the group name of the stream
+ * @param name the name of the stream
+ * @return true if the stream is deleted successfully
+ */
+ public boolean deleteStream(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ StreamMetadataRegistry streamRegistry = new StreamMetadataRegistry(checkNotNull(this.channel));
+ return streamRegistry.delete(group, name);
+ }
+
+ /**
+ * Delete a measure
+ * @param group the group name of the measure
+ * @param name the name of the measure
+ * @return true if the measure is deleted successfully
+ */
+ public boolean deleteMeasure(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ MeasureMetadataRegistry measureRegistry = new MeasureMetadataRegistry(checkNotNull(this.channel));
+ return measureRegistry.delete(group, name);
+ }
+
+ /**
+ * Delete the TopNAggregation
+ * @param group the group name of the topN rule
+ * @param name the name of the topN rule
+ * @return true if the topN rule is deleted successfully
+ */
+ public boolean deleteTopNAggregation(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ TopNAggregationMetadataRegistry registry = new TopNAggregationMetadataRegistry(checkNotNull(this.channel));
+ return registry.delete(group, name);
+ }
+
+ /**
+ * Delete the IndexRule
+ * @param group the group name of the index rule
+ * @param name the name of the index rule
+ * @return true if the index rule is deleted successfully
+ */
+ public boolean deleteIndexRule(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ IndexRuleMetadataRegistry registry = new IndexRuleMetadataRegistry(checkNotNull(this.channel));
+ return registry.delete(group, name);
+ }
+
+ /**
+ * Delete the IndexRuleBinding
+ * @param group the group name of the index rule binding
+ * @param name the name of the index rule binding
+ * @return true if the index rule binding is deleted successfully
+ */
+ public boolean deleteIndexRuleBinding(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ IndexRuleBindingMetadataRegistry registry = new IndexRuleBindingMetadataRegistry(checkNotNull(this.channel));
+ return registry.delete(group, name);
+ }
+
+ /**
+ * Find the IndexRule
+ * @param group the group name of the index rule
+ * @param name the name of the index rule
+ * @return the index rule if it can be found, otherwise null
+ */
+ public IndexRule findIndexRule(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ IndexRuleMetadataRegistry registry = new IndexRuleMetadataRegistry(checkNotNull(this.channel));
+ try {
+ return registry.get(group, name);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.NOT_FOUND)) {
+ return null;
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Find all IndexRules in the group
+ * @param group the group name of the index rule
+ * @return all index rules in the group
+ */
+ public List<IndexRule> findIndexRules(String group) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ IndexRuleMetadataRegistry registry = new IndexRuleMetadataRegistry(checkNotNull(this.channel));
+ return registry.list(group);
+ }
+
+ /**
+ * Find the IndexRuleBinding
+ * @param group the group name of the index rule binding
+ * @param name the name of the index rule binding
+ * @return the index rule binding if it can be found, otherwise null
+ */
+ public IndexRuleBinding findIndexRuleBinding(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ IndexRuleBindingMetadataRegistry registry = new IndexRuleBindingMetadataRegistry(checkNotNull(this.channel));
+ try {
+ return registry.get(group, name);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.NOT_FOUND)) {
+ return null;
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Find all IndexRuleBindings in the group
+ * @param group the group name of the index rule binding
+ * @return all index rule bindings in the group
+ */
+ public List<IndexRuleBinding> findIndexRuleBindings(String group) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ IndexRuleBindingMetadataRegistry registry = new IndexRuleBindingMetadataRegistry(checkNotNull(this.channel));
+ return registry.list(group);
+ }
+
+ /**
+ * Define a new property.
+ *
+ * @param property the property to be stored in the BanyanBD
+ * @throws BanyanDBException if the property is invalid
+ */
+ public void define(org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property property) throws BanyanDBException {
+ PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
+ registry.create(property);
+ }
+
+ /**
+ * Update the property.
+ *
+ * @param property the property to be stored in the BanyanBD
+ * @throws BanyanDBException if the property is invalid
+ */
+ public void update(org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property property) throws BanyanDBException {
+ PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
+ registry.update(property);
+ }
+
+ /**
+ * Find the property with given group and name
+ *
+ * @param group group of the metadata
+ * @param name name of the metadata
+ * @return the property found in BanyanDB. Otherwise, null is returned.
+ */
+ public org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property findPropertyDefinition(String group, String name) throws BanyanDBException {
+ PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
+ return registry.get(group, name);
+ }
+
+ /**
+ * Find the properties with given group
+ *
+ * @param group group of the metadata
+ * @return the properties found in BanyanDB
+ */
+ public List<org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property> findPropertiesDefinition(String group) throws BanyanDBException {
+ PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
+ return registry.list(group);
+ }
+
+ /**
+ * Delete the property
+ *
+ * @param group group of the metadata
+ * @param name name of the metadata
+ * @return if this property has been deleted
+ */
+ public boolean deletePropertyDefinition(String group, String name) throws BanyanDBException {
+ PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
+ return registry.delete(group, name);
+ }
+
+ /**
+ * Query properties
+ *
+ * @param request query request
+ * @return query response
+ */
+ public BanyandbProperty.QueryResponse query(BanyandbProperty.QueryRequest request) throws BanyanDBException {
+ PropertyStore store = new PropertyStore(checkNotNull(this.channel));
+ return store.query(request);
+ }
+
+ /**
+ * Define a new trace
+ *
+ * @param trace the trace to be stored in the BanyanDB
+ * @throws BanyanDBException if the trace is invalid
+ */
+ public void define(Trace trace) throws BanyanDBException {
+ TraceMetadataRegistry registry = new TraceMetadataRegistry(checkNotNull(this.channel));
+ registry.create(trace);
+ }
+
+ /**
+ * Update the trace.
+ *
+ * @param trace the trace to be stored in the BanyanDB
+ * @throws BanyanDBException if the trace is invalid
+ */
+ public void update(Trace trace) throws BanyanDBException {
+ TraceMetadataRegistry registry = new TraceMetadataRegistry(checkNotNull(this.channel));
+ registry.update(trace);
+ }
+
+ /**
+ * Find the trace with given group and name
+ *
+ * @param group group of the metadata
+ * @param name name of the metadata
+ * @return the trace found in BanyanDB. Otherwise, null is returned.
+ */
+ public Trace findTrace(String group, String name) throws BanyanDBException {
+ try {
+ return new TraceMetadataRegistry(checkNotNull(this.channel)).get(group, name);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.NOT_FOUND)) {
+ return null;
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Find the traces with given group
+ *
+ * @param group group of the metadata
+ * @return the traces found in BanyanDB
+ */
+ public List<Trace> findTraces(String group) throws BanyanDBException {
+ TraceMetadataRegistry registry = new TraceMetadataRegistry(checkNotNull(this.channel));
+ return registry.list(group);
+ }
+
+ /**
+ * Delete the trace
+ *
+ * @param group group of the metadata
+ * @param name name of the metadata
+ * @return if this trace has been deleted
+ */
+ public boolean deleteTrace(String group, String name) throws BanyanDBException {
+ TraceMetadataRegistry registry = new TraceMetadataRegistry(checkNotNull(this.channel));
+ return registry.delete(group, name);
+ }
+
+ /**
+ * Try to find the group defined
+ *
+ * @param name name of the group
+ * @return the group found in BanyanDB. Otherwise, null is returned.
+ */
+ public Group findGroup(String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+
+ try {
+ return new GroupMetadataRegistry(checkNotNull(this.channel)).get(name, name);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.NOT_FOUND)) {
+ return null;
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Try to find the groups defined
+ *
+ * @return the groups found in BanyanDB
+ */
+ public List<Group> findGroups() throws BanyanDBException {
+ return new GroupMetadataRegistry(checkNotNull(this.channel)).list("");
+ }
+
+ /**
+ * Try to find the TopNAggregation from the BanyanDB with given group and name.
+ *
+ * @param group group of the TopNAggregation
+ * @param name name of the TopNAggregation
+ * @return TopNAggregation if found. Otherwise, null is returned.
+ */
+ public TopNAggregation findTopNAggregation(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ try {
+ return new TopNAggregationMetadataRegistry(checkNotNull(this.channel)).get(group, name);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.NOT_FOUND)) {
+ return null;
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Try to find the TopNAggregations from the BanyanDB with given group.
+ *
+ * @param group group of the TopNAggregations
+ * @return TopNAggregations if found.
+ */
+ public List<TopNAggregation> findTopNAggregations(String group) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ return new TopNAggregationMetadataRegistry(checkNotNull(this.channel)).list(group);
+ }
+
+ /**
+ * Try to find the stream from the BanyanDB with given group and name.
+ *
+ * @param group group of the stream
+ * @param name name of the stream
+ * @return Steam if found. Otherwise, null is returned.
+ */
+ public Stream findStream(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ try {
+ return new StreamMetadataRegistry(checkNotNull(this.channel)).get(group, name);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.NOT_FOUND)) {
+ return null;
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Try to find the streams from the BanyanDB with given group.
+ *
+ * @param group group of the streams
+ * @return Streams if found.
+ */
+ public List<Stream> findStreams(String group) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ StreamMetadataRegistry registry = new StreamMetadataRegistry(checkNotNull(this.channel));
+ return registry.list(group);
+ }
+
+ /**
+ * Try to find the measure from the BanyanDB with given group and name.
+ *
+ * @param group group of the measure
+ * @param name name of the measure
+ * @return Measure with index rules if found. Otherwise, null is returned.
+ */
+ public Measure findMeasure(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+ try {
+ return new MeasureMetadataRegistry(checkNotNull(this.channel)).get(group, name);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.NOT_FOUND)) {
+ return null;
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Try to find the measures from the BanyanDB with given group.
+ *
+ * @param group group of the measures
+ * @return Measures if found.
+ */
+ public List<Measure> findMeasures(String group) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ MeasureMetadataRegistry registry = new MeasureMetadataRegistry(checkNotNull(this.channel));
+ return registry.list(group);
+ }
+
+ private List<IndexRule> findIndexRulesByGroupAndBindingName(String group, String bindingName) throws
+ BanyanDBException {
+ IndexRuleBindingMetadataRegistry irbRegistry = new IndexRuleBindingMetadataRegistry(checkNotNull(this.channel));
+
+ IndexRuleBinding irb;
+ try {
+ irb = irbRegistry.get(group, bindingName);
+ } catch (BanyanDBException ex) {
+ if (ex.getStatus().equals(Status.Code.NOT_FOUND)) {
+ return Collections.emptyList();
+ }
+ throw ex;
+ }
+
+ if (irb == null) {
+ return Collections.emptyList();
+ }
+
+ List<IndexRule> indexRules = new ArrayList<>(irb.getRulesList().size());
+ return indexRules;
+ }
+
+ /**
+ * Check if the given stream exists.
+ *
+ * @param group group of the stream
+ * @param name name of the stream
+ * @return ResourceExist which indicates whether group and stream exist
+ */
+ public ResourceExist existStream(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+
+ return new StreamMetadataRegistry(checkNotNull(this.channel)).exist(group, name);
+ }
+
+ /**
+ * Check if the given measure exists.
+ *
+ * @param group group of the measure
+ * @param name name of the measure
+ * @return ResourceExist which indicates whether group and measure exist
+ */
+ public ResourceExist existMeasure(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+
+ return new MeasureMetadataRegistry(checkNotNull(this.channel)).exist(group, name);
+ }
+
+ /**
+ * Check if the given TopNAggregation exists.
+ *
+ * @param group group of the TopNAggregation
+ * @param name name of the TopNAggregation
+ * @return ResourceExist which indicates whether group and TopNAggregation exist
+ */
+ public ResourceExist existTopNAggregation(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+
+ return new TopNAggregationMetadataRegistry(checkNotNull(this.channel)).exist(group, name);
+ }
+
+ /**
+ * Check if the given property exists.
+ *
+ * @param group group of the property
+ * @param name name of the property
+ * @return ResourceExist which indicates whether group and property exist
+ */
+ public ResourceExist existProperty(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+
+ return new PropertyMetadataRegistry(checkNotNull(this.channel)).exist(group, name);
+ }
+
+ /**
+ * Check whether the trace definition is existed in the server
+ *
+ * @param group group of the metadata
+ * @param name name of the metadata
+ * @return ResourceExist which indicates whether group and trace exist
+ */
+ public ResourceExist existTrace(String group, String name) throws BanyanDBException {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+
+ return new TraceMetadataRegistry(checkNotNull(this.channel)).exist(group, name);
+ }
+
+ /**
+ * Get the API version of the server
+ *
+ * @return the API version of the server
+ * @throws BanyanDBException if the server is not reachable
+ */
+ public BanyandbCommon.APIVersion getAPIVersion() throws BanyanDBException {
+ ServiceGrpc.ServiceBlockingStub stub = ServiceGrpc.newBlockingStub(this.channel);
+ return HandleExceptionsWith.callAndTranslateApiException(() -> {
+ BanyandbCommon.GetAPIVersionResponse resp = stub.getAPIVersion(BanyandbCommon.GetAPIVersionRequest.getDefaultInstance());
+ return resp.getVersion();
+ });
+ }
+
+ @Override
+ public void close() throws IOException {
+ connectionEstablishLock.lock();
+ if (!(this.channel instanceof ManagedChannel)) {
+ return;
+ }
+ final ManagedChannel managedChannel = (ManagedChannel) this.channel;
+ try {
+ if (isConnected) {
+ managedChannel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
+ isConnected = false;
+ }
+ } catch (InterruptedException interruptedException) {
+ Thread.currentThread().interrupt();
+ log.warn("fail to wait for channel termination, shutdown now!", interruptedException);
+ managedChannel.shutdownNow();
+ isConnected = false;
+ } finally {
+ connectionEstablishLock.unlock();
+ }
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/DataPoint.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/DataPoint.java
new file mode 100644
index 0000000000..f07c615ed4
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/DataPoint.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.measure.v1.BanyandbMeasure;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+
+/**
+ * RowEntity represents an entity of BanyanDB entity.
+ */
+@Getter
+public class DataPoint extends RowEntity {
+ private final Map<String, Object> fields;
+
+ public static DataPoint create(BanyandbMeasure.DataPoint dataPoint) {
+ return new DataPoint(dataPoint);
+ }
+
+ private DataPoint(BanyandbMeasure.DataPoint dataPoint) {
+ super(dataPoint.getTimestamp(), dataPoint.getTagFamiliesList());
+ this.fields = new HashMap<>(dataPoint.getFieldsCount());
+ for (BanyandbMeasure.DataPoint.Field f : dataPoint.getFieldsList()) {
+ this.fields.put(f.getName(), convertFileValueToJavaType(f.getValue()));
+ }
+ }
+
+ public <T> T getFieldValue(String fieldName) {
+ return (T) this.fields.get(fieldName);
+ }
+
+ static Object convertFileValueToJavaType(BanyandbModel.FieldValue fieldValue) {
+ switch (fieldValue.getValueCase()) {
+ case INT:
+ return fieldValue.getInt().getValue();
+ case STR:
+ return fieldValue.getStr().getValue();
+ case NULL:
+ return null;
+ case BINARY_DATA:
+ return fieldValue.getBinaryData().toByteArray();
+ case FLOAT:
+ return fieldValue.getFloat().getValue();
+ default:
+ throw new IllegalStateException("illegal type of FieldValue");
+ }
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Element.java
similarity index 55%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Element.java
index dbef0e0068..00a4c9f295 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Element.java
@@ -16,20 +16,28 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import org.apache.skywalking.banyandb.stream.v1.BanyandbStream;
-@RequiredArgsConstructor
+/**
+ * Element represents an entity in a Stream.
+ */
@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
+public class Element extends RowEntity {
+ /**
+ * identity of the element.
+ * For a trace entity, it is the spanID of a Span or the segmentId of a segment in Skywalking,
+ */
+ protected final String id;
- @Override
- public void onInsertCompleted() {
+ public static Element create(BanyandbStream.Element element) {
+ return new Element(element);
+ }
+ private Element(BanyandbStream.Element element) {
+ super(element.getTimestamp(), element.getTagFamiliesList());
+ this.id = element.getElementId();
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureQuery.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureQuery.java
new file mode 100644
index 0000000000..5b0251c349
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureQuery.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import org.apache.skywalking.banyandb.measure.v1.BanyandbMeasure;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+/**
+ * MeasureQuery is the high-level query API for the measure model.
+ */
+@Setter
+public class MeasureQuery extends AbstractQuery<BanyandbMeasure.QueryRequest> {
+ /**
+ * field_projection can be used to select fields of the data points in the response
+ */
+ private final Set<String> fieldProjections;
+
+ private Aggregation aggregation;
+
+ private TopN topN;
+
+ private Integer limit;
+
+ private int offset;
+
+ private OrderBy orderBy;
+
+ private Set<String> stages;
+
+ public MeasureQuery(final List<String> groups,
+ final String name,
+ final Map<String/*tagName*/, String/*tagFamilyName*/> tagProjections,
+ final Set<String/*tagName*/> fieldProjections) {
+ this(groups, name, null, tagProjections, fieldProjections);
+ }
+
+ public MeasureQuery(final List<String> groups,
+ final String name,
+ final TimestampRange timestampRange,
+ final Map<String/*tagName*/, String/*tagFamilyName*/> tagProjections,
+ final Set<String/*tagName*/> fieldProjections) {
+ super(groups, name, timestampRange, tagProjections);
+ this.fieldProjections = fieldProjections;
+ }
+
+ @Override
+ public MeasureQuery and(PairQueryCondition<?> condition) {
+ return (MeasureQuery) super.and(condition);
+ }
+
+ @Override
+ public MeasureQuery or(PairQueryCondition<?> condition) {
+ return (MeasureQuery) super.or(condition);
+ }
+
+ public MeasureQuery groupBy(Set<String> groupByKeys) {
+ Preconditions.checkArgument(tagProjections.keySet().containsAll(groupByKeys), "groupBy tags should be selected first");
+ this.aggregation = new Aggregation(null, Aggregation.Type.UNSPECIFIED, groupByKeys);
+ return this;
+ }
+
+ public MeasureQuery maxBy(String field, Set<String> groupByKeys) {
+ Preconditions.checkArgument(fieldProjections.contains(field), "field should be selected first");
+ Preconditions.checkArgument(tagProjections.keySet().containsAll(groupByKeys), "groupBy tags should be selected first");
+ this.aggregation = new Aggregation(field, Aggregation.Type.MAX, groupByKeys);
+ return this;
+ }
+
+ public MeasureQuery minBy(String field, Set<String> groupByKeys) {
+ Preconditions.checkArgument(fieldProjections.contains(field), "field should be selected first");
+ Preconditions.checkArgument(tagProjections.keySet().containsAll(groupByKeys), "groupBy tags should be selected first");
+ Preconditions.checkState(this.aggregation == null, "aggregation should only be set once");
+ this.aggregation = new Aggregation(field, Aggregation.Type.MIN, groupByKeys);
+ return this;
+ }
+
+ public MeasureQuery meanBy(String field, Set<String> groupByKeys) {
+ Preconditions.checkArgument(fieldProjections.contains(field), "field should be selected first");
+ Preconditions.checkArgument(tagProjections.keySet().containsAll(groupByKeys), "groupBy tags should be selected first");
+ Preconditions.checkState(this.aggregation == null, "aggregation should only be set once");
+ this.aggregation = new Aggregation(field, Aggregation.Type.MEAN, groupByKeys);
+ return this;
+ }
+
+ public MeasureQuery countBy(String field, Set<String> groupByKeys) {
+ Preconditions.checkArgument(fieldProjections.contains(field), "field should be selected first");
+ Preconditions.checkArgument(tagProjections.keySet().containsAll(groupByKeys), "groupBy tags should be selected first");
+ Preconditions.checkState(this.aggregation == null, "aggregation should only be set once");
+ this.aggregation = new Aggregation(field, Aggregation.Type.COUNT, groupByKeys);
+ return this;
+ }
+
+ public MeasureQuery sumBy(String field, Set<String> groupByKeys) {
+ Preconditions.checkArgument(fieldProjections.contains(field), "field should be selected first");
+ Preconditions.checkArgument(tagProjections.keySet().containsAll(groupByKeys), "groupBy tags should be selected first");
+ Preconditions.checkState(this.aggregation == null, "aggregation should only be set once");
+ this.aggregation = new Aggregation(field, Aggregation.Type.SUM, groupByKeys);
+ return this;
+ }
+
+ public MeasureQuery topN(int number, String field) {
+ Preconditions.checkArgument(fieldProjections.contains(field), "field should be selected first");
+ this.topN = new TopN(field, number, Sort.DESC);
+ return this;
+ }
+
+ public MeasureQuery bottomN(int number, String field) {
+ Preconditions.checkArgument(fieldProjections.contains(field), "field should be selected first");
+ this.topN = new TopN(field, number, Sort.ASC);
+ return this;
+ }
+
+ public MeasureQuery limit(int limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ public MeasureQuery offset(int offset) {
+ this.offset = offset;
+ return this;
+ }
+
+ public MeasureQuery stages(Set<String> stages) {
+ this.stages = stages;
+ return this;
+ }
+
+ /**
+ * @return QueryRequest for gRPC level query.
+ */
+ @Override
+ BanyandbMeasure.QueryRequest build() throws BanyanDBException {
+ final BanyandbMeasure.QueryRequest.Builder builder = BanyandbMeasure.QueryRequest.newBuilder();
+ builder.setName(this.name);
+ builder.addAllGroups(this.groups);
+ if (timestampRange != null) {
+ builder.setTimeRange(timestampRange.build());
+ } else {
+ builder.setTimeRange(TimestampRange.MAX_RANGE);
+ }
+ BanyandbModel.TagProjection tagProjections = buildTagProjections();
+ if (tagProjections.getTagFamiliesCount() > 0) {
+ builder.setTagProjection(buildTagProjections());
+ }
+ if (!fieldProjections.isEmpty()) {
+ builder.setFieldProjection(BanyandbMeasure.QueryRequest.FieldProjection.newBuilder()
+ .addAllNames(fieldProjections)
+ .build());
+ }
+ if (stages != null && !stages.isEmpty()) {
+ builder.addAllStages(stages);
+ }
+ if (this.aggregation != null) {
+ BanyandbMeasure.QueryRequest.GroupBy.Builder groupByBuilder = BanyandbMeasure.QueryRequest.GroupBy.newBuilder()
+ .setTagProjection(buildTagProjections(this.aggregation.groupByTagsProjection));
+ if (Strings.isNullOrEmpty(this.aggregation.fieldName)) {
+ if (this.aggregation.aggregationType != Aggregation.Type.UNSPECIFIED) {
+ throw new IllegalArgumentException("field name cannot be null or empty if aggregation is specified");
+ }
+ } else {
+ groupByBuilder.setFieldName(this.aggregation.fieldName);
+ BanyandbMeasure.QueryRequest.Aggregation aggr = BanyandbMeasure.QueryRequest.Aggregation.newBuilder()
+ .setFunction(this.aggregation.aggregationType.function)
+ .setFieldName(this.aggregation.fieldName)
+ .build();
+ builder.setGroupBy(groupByBuilder.build()).setAgg(aggr);
+ }
+ }
+ if (this.topN != null) {
+ BanyandbMeasure.QueryRequest.Top top = BanyandbMeasure.QueryRequest.Top.newBuilder()
+ .setFieldName(this.topN.fieldName)
+ .setNumber(this.topN.number)
+ .setFieldValueSort(Sort.DESC.equals(this.topN.sort) ? BanyandbModel.Sort.SORT_DESC : BanyandbModel.Sort.SORT_ASC)
+ .build();
+ builder.setTop(top);
+ }
+ builder.setOffset(this.offset);
+ if (this.limit != null) {
+ builder.setLimit(this.limit);
+ }
+ if (this.orderBy != null) {
+ builder.setOrderBy(orderBy.build());
+ }
+ // add all criteria
+ buildCriteria().ifPresent(builder::setCriteria);
+ builder.setTrace(this.trace);
+ return builder.build();
+ }
+
+ @RequiredArgsConstructor
+ public static class TopN {
+ private final String fieldName;
+ private final int number;
+ private final AbstractQuery.Sort sort;
+ }
+
+ @RequiredArgsConstructor
+ public static class Aggregation {
+ private final String fieldName;
+ private final Type aggregationType;
+ private final Set<String> groupByTagsProjection;
+
+ @RequiredArgsConstructor
+ public enum Type {
+ UNSPECIFIED(BanyandbModel.AggregationFunction.AGGREGATION_FUNCTION_UNSPECIFIED),
+ MEAN(BanyandbModel.AggregationFunction.AGGREGATION_FUNCTION_MEAN),
+ MAX(BanyandbModel.AggregationFunction.AGGREGATION_FUNCTION_MAX),
+ MIN(BanyandbModel.AggregationFunction.AGGREGATION_FUNCTION_MIN),
+ COUNT(BanyandbModel.AggregationFunction.AGGREGATION_FUNCTION_COUNT),
+ SUM(BanyandbModel.AggregationFunction.AGGREGATION_FUNCTION_SUM);
+ final BanyandbModel.AggregationFunction function;
+ }
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureQueryResponse.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureQueryResponse.java
new file mode 100644
index 0000000000..fbc4c5d4dd
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureQueryResponse.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.measure.v1.BanyandbMeasure;
+
+/**
+ * MeasureQueryResponse represents the measure query result.
+ */
+public class MeasureQueryResponse {
+ @Getter
+ private final List<DataPoint> dataPoints;
+
+ @Getter
+ private final Trace trace;
+
+ MeasureQueryResponse(BanyandbMeasure.QueryResponse response) {
+ final List<BanyandbMeasure.DataPoint> dataPointList = response.getDataPointsList();
+ this.dataPoints = new ArrayList<>(dataPointList.size());
+ for (final BanyandbMeasure.DataPoint dp : dataPointList) {
+ dataPoints.add(DataPoint.create(dp));
+ }
+ this.trace = Trace.convertFromProto(response.getTrace());
+ }
+
+ /**
+ * @return size of the response set.
+ */
+ public int size() {
+ return dataPoints.size();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureWrite.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureWrite.java
new file mode 100644
index 0000000000..477e21ef65
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/MeasureWrite.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.protobuf.Timestamp;
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.measure.v1.BanyandbMeasure;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.Serializable;
+
+public class MeasureWrite extends AbstractWrite<BanyandbMeasure.WriteRequest> {
+
+ // Use TreeMap to have a consistent order for fields
+ private final Map<String/*fieldName*/, Serializable<BanyandbModel.FieldValue>/*fieldValue*/> fields;
+
+ // Use TreeMap to have a consistent order for tags
+ private final Map<String/*tagFamily*/, Map<String/*tagName*/, Serializable<BanyandbModel.TagValue>/*tagValue*/>> tags;
+
+ MeasureWrite(BanyandbCommon.Metadata entityMetadata, long timestamp) {
+ super(entityMetadata, timestamp);
+ this.fields = new TreeMap<>();
+ this.tags = new TreeMap<>();
+ }
+
+ public MeasureWrite field(String fieldName, Serializable<BanyandbModel.FieldValue> fieldVal) {
+ this.fields.put(fieldName, fieldVal);
+ return this;
+ }
+
+ public MeasureWrite tag(String tagFamilyName, String tagName, Serializable<BanyandbModel.TagValue> tagValue) throws BanyanDBException {
+ this.tags.computeIfAbsent(tagFamilyName, k -> new TreeMap<>()).put(tagName, tagValue);
+ return this;
+ }
+
+ /**
+ * Build a write request
+ *
+ * @return {@link BanyandbMeasure.WriteRequest} for the bulk process.
+ */
+ @Override
+ protected BanyandbMeasure.WriteRequest build(BanyandbCommon.Metadata metadata) {
+ if (!timestamp.isPresent() || timestamp.get() <= 0) {
+ throw new IllegalArgumentException("Timestamp is required and must be greater than 0 for stream writes.");
+ }
+ Timestamp ts = Timestamp.newBuilder()
+ .setSeconds(timestamp.get() / 1000)
+ .setNanos((int) (timestamp.get() % 1000 * 1_000_000)).build();
+
+ final BanyandbMeasure.WriteRequest.Builder builder = BanyandbMeasure.WriteRequest.newBuilder();
+ builder.setMetadata(metadata);
+ final BanyandbMeasure.DataPointValue.Builder datapointValueBuilder = BanyandbMeasure.DataPointValue.newBuilder();
+ final BanyandbMeasure.DataPointSpec.Builder datapointValueSpecBuilder = BanyandbMeasure.DataPointSpec.newBuilder();
+ datapointValueBuilder.setTimestamp(ts);
+ for (Map.Entry<String, Map<String, Serializable<BanyandbModel.TagValue>>> tagFamilyEntry : this.tags.entrySet()) {
+ BanyandbMeasure.TagFamilySpec.Builder tagFamilySpecBuilder = BanyandbMeasure.TagFamilySpec.newBuilder();
+ BanyandbModel.TagFamilyForWrite.Builder tagFamilyForWriteBuilder = BanyandbModel.TagFamilyForWrite.newBuilder();
+ tagFamilySpecBuilder.setName(tagFamilyEntry.getKey());
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : tagFamilyEntry.getValue().entrySet()) {
+ tagFamilySpecBuilder.addTagNames(tagEntry.getKey());
+ tagFamilyForWriteBuilder.addTags(tagEntry.getValue().serialize());
+ }
+ datapointValueSpecBuilder.addTagFamilySpec(tagFamilySpecBuilder);
+ datapointValueBuilder.addTagFamilies(tagFamilyForWriteBuilder);
+ }
+
+ for (Map.Entry<String, Serializable<BanyandbModel.FieldValue>> fieldEntry : this.fields.entrySet()) {
+ datapointValueSpecBuilder.addFieldNames(fieldEntry.getKey());
+ datapointValueBuilder.addFields(fieldEntry.getValue().serialize());
+ }
+
+ builder.setDataPointSpec(datapointValueSpecBuilder);
+ builder.setDataPoint(datapointValueBuilder);
+ builder.setMessageId(System.nanoTime());
+ return builder.build();
+ }
+
+ /**
+ * Build a write request without metadata and tag specs.
+ *
+ * @return {@link BanyandbMeasure.WriteRequest} for the bulk process.
+ */
+ @Override
+ protected BanyandbMeasure.WriteRequest buildValues() {
+ if (!timestamp.isPresent() || timestamp.get() <= 0) {
+ throw new IllegalArgumentException("Timestamp is required and must be greater than 0 for stream writes.");
+ }
+ Timestamp ts = Timestamp.newBuilder()
+ .setSeconds(timestamp.get() / 1000)
+ .setNanos((int) (timestamp.get() % 1000 * 1_000_000)).build();
+
+ final BanyandbMeasure.WriteRequest.Builder builder = BanyandbMeasure.WriteRequest.newBuilder();
+ final BanyandbMeasure.DataPointValue.Builder datapointValueBuilder = BanyandbMeasure.DataPointValue.newBuilder();
+ datapointValueBuilder.setTimestamp(ts);
+ for (Map.Entry<String, Map<String, Serializable<BanyandbModel.TagValue>>> tagFamilyEntry : this.tags.entrySet()) {
+ BanyandbModel.TagFamilyForWrite.Builder tagFamilyForWriteBuilder = BanyandbModel.TagFamilyForWrite.newBuilder();
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : tagFamilyEntry.getValue().entrySet()) {
+ tagFamilyForWriteBuilder.addTags(tagEntry.getValue().serialize());
+ }
+ datapointValueBuilder.addTagFamilies(tagFamilyForWriteBuilder);
+ }
+
+ for (Map.Entry<String, Serializable<BanyandbModel.FieldValue>> fieldEntry : this.fields.entrySet()) {
+ datapointValueBuilder.addFields(fieldEntry.getValue().serialize());
+ }
+
+ builder.setDataPoint(datapointValueBuilder);
+ builder.setMessageId(System.nanoTime());
+ return builder.build();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("group=").append(entityMetadata.getGroup()).append(", ").append("name=")
+ .append(entityMetadata.getName()).append(", ").append("timestamp=").append(timestamp).append(", ");
+ for (Map.Entry<String, Map<String, Serializable<BanyandbModel.TagValue>>> entry : tags.entrySet()) {
+ String tagFamilyName = entry.getKey();
+ Map<String, Serializable<BanyandbModel.TagValue>> tagMap = entry.getValue();
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : tagMap.entrySet()) {
+ String tagName = tagEntry.getKey();
+ Serializable<BanyandbModel.TagValue> tagValue = tagEntry.getValue();
+ stringBuilder.append(tagFamilyName).append(".").append(tagName).append("=")
+ .append(tagValue.serialize()).append(", ");
+ }
+ }
+ for (Map.Entry<String, Serializable<BanyandbModel.FieldValue>> fieldEntry : fields.entrySet()) {
+ String fieldName = fieldEntry.getKey();
+ Serializable<BanyandbModel.FieldValue> fieldValue = fieldEntry.getValue();
+ stringBuilder.append("field.").append(fieldName).append("=")
+ .append(fieldValue.serialize()).append(", ");
+ }
+ return stringBuilder.toString();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Options.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Options.java
new file mode 100644
index 0000000000..76f89cba83
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Options.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.channel.ChannelManagerSettings;
+
+/**
+ * Client connection options.
+ */
+@Setter(AccessLevel.PUBLIC)
+@Getter(AccessLevel.PUBLIC)
+public class Options {
+ /**
+ * Max inbound message size
+ */
+ private int maxInboundMessageSize = 1024 * 1024 * 50;
+ /**
+ * Threshold of gRPC blocking query, unit is second
+ */
+ private int deadline = 30;
+ /**
+ * Refresh interval for the gRPC channel, unit is second
+ */
+ private long refreshInterval = 30;
+ /**
+ * Threshold of force gRPC reconnection if network issue is encountered
+ */
+ private long forceReconnectionThreshold = 1;
+
+ /**
+ * Force use TLS for gRPC
+ * Default is false
+ */
+ private boolean forceTLS = false;
+ /**
+ * SSL: Trusted CA Path
+ */
+ private String sslTrustCAPath = "";
+ /**
+ * Basic Auth: username of BanyanDB server
+ */
+ private String username = "";
+ /**
+ * Basic Auth: password of BanyanDB server
+ */
+ private String password = "";
+ /**
+ * Enable Prometheus metrics
+ */
+ private PrometheusMetricsOpts prometheusMetricsOpts = new PrometheusMetricsOpts();
+
+ public Options() {
+ }
+
+ ChannelManagerSettings buildChannelManagerSettings() {
+ return ChannelManagerSettings.builder()
+ .refreshInterval(this.refreshInterval)
+ .forceReconnectionThreshold(this.forceReconnectionThreshold)
+ .build();
+ }
+
+ public static class PrometheusMetricsOpts {
+ @Setter(AccessLevel.PUBLIC)
+ @Getter
+ private String clientID = "default";
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Or.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Or.java
new file mode 100644
index 0000000000..32806b8e51
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Or.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import lombok.Builder;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+
+@Builder
+@Getter
+public class Or extends LogicalExpression {
+ private final AbstractCriteria left;
+ private final AbstractCriteria right;
+
+ @Override
+ public BanyandbModel.Criteria build() {
+ return BanyandbModel.Criteria.newBuilder()
+ .setLe(BanyandbModel.LogicalExpression.newBuilder()
+ .setLeft(left.build())
+ .setRight(right.build())
+ .setOp(BanyandbModel.LogicalExpression.LogicalOp.LOGICAL_OP_OR)
+ .build())
+ .build();
+ }
+
+ public static Or create(AbstractCriteria left, AbstractCriteria right) {
+ return Or.builder().left(left).right(right).build();
+ }
+
+ @Override
+ AbstractCriteria left() {
+ return left;
+ }
+
+ @Override
+ AbstractCriteria right() {
+ return right;
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PairQueryCondition.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PairQueryCondition.java
new file mode 100644
index 0000000000..df843bbee4
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PairQueryCondition.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.List;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel.Condition.MatchOption;
+
+/**
+ * PairQuery represents a query condition, including tag name, operator, and value(s);
+ */
+public abstract class PairQueryCondition<T> extends AbstractCriteria {
+ protected final BanyandbModel.Condition.BinaryOp op;
+ private final TagAndValue<T> tagAndValue;
+ private final MatchOption matchOption;
+
+ private PairQueryCondition(BanyandbModel.Condition.BinaryOp op, TagAndValue<T> tagAndValue) {
+ this.op = op;
+ this.tagAndValue = tagAndValue;
+ this.matchOption = MatchOption.getDefaultInstance();
+ }
+
+ private PairQueryCondition(BanyandbModel.Condition.BinaryOp op,
+ TagAndValue<T> tagAndValue,
+ MatchOption matchOption) {
+ this.op = op;
+ this.tagAndValue = tagAndValue;
+ this.matchOption = matchOption;
+ }
+
+ @Override
+ public BanyandbModel.Criteria build() {
+ BanyandbModel.Condition.Builder condition = BanyandbModel.Condition.newBuilder()
+ .setName(this.tagAndValue.getTagName())
+ .setOp(this.op)
+ .setValue(this.tagAndValue.buildTypedTagValue());
+ if (this.matchOption != null) {
+ condition.setMatchOption(this.matchOption);
+ }
+ return BanyandbModel.Criteria.newBuilder()
+ .setCondition(condition.build())
+ .build();
+ }
+
+ public String getTagName() {
+ return this.tagAndValue.getTagName();
+ }
+
+ /**
+ * LongQueryCondition represents `tag(Long) $op value` condition.
+ */
+ public static class LongQueryCondition extends PairQueryCondition<Long> {
+ private LongQueryCondition(String tagName, BanyandbModel.Condition.BinaryOp op, Long value) {
+ super(op, new TagAndValue.LongTagPair(tagName, value));
+ }
+
+ /**
+ * Build a query condition for {@link Long} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_EQ} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `Long == value`
+ */
+ public static PairQueryCondition<Long> eq(String tagName, Long val) {
+ return new LongQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_EQ, val);
+ }
+
+ /**
+ * Build a query condition for {@link Long} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_NE} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `Long != value`
+ */
+ public static PairQueryCondition<Long> ne(String tagName, Long val) {
+ return new LongQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_NE, val);
+ }
+
+ /**
+ * Build a query condition for {@link Long} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_GT} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `Long > value`
+ */
+ public static PairQueryCondition<Long> gt(String tagName, Long val) {
+ return new LongQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_GT, val);
+ }
+
+ /**
+ * Build a query condition for {@link Long} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_GE} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `Long ≥ value`
+ */
+ public static PairQueryCondition<Long> ge(String tagName, Long val) {
+ return new LongQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_GE, val);
+ }
+
+ /**
+ * Build a query condition for {@link Long} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_LT} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `Long < value`
+ */
+ public static PairQueryCondition<Long> lt(String tagName, Long val) {
+ return new LongQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_LT, val);
+ }
+
+ /**
+ * Build a query condition for {@link Long} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_LE} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `Long ≤ value`
+ */
+ public static PairQueryCondition<Long> le(String tagName, Long val) {
+ return new LongQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_LE, val);
+ }
+
+ }
+
+ /**
+ * StringQueryCondition represents `tag(String) $op value` condition.
+ */
+ public static class StringQueryCondition extends PairQueryCondition<String> {
+ private StringQueryCondition(String tagName, BanyandbModel.Condition.BinaryOp op, String value) {
+ super(op, new TagAndValue.StringTagPair(tagName, value));
+ }
+
+ private StringQueryCondition(String tagName, BanyandbModel.Condition.BinaryOp op, String value, MatchOption matchOption) {
+ super(op, new TagAndValue.StringTagPair(tagName, value), matchOption);
+ }
+
+ /**
+ * Build a query condition for {@link String} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_EQ} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `String == value`
+ */
+ public static PairQueryCondition<String> eq(String tagName, String val) {
+ return new StringQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_EQ, val);
+ }
+
+ /**
+ * Build a query condition for {@link String} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_NE} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `String != value`
+ */
+ public static PairQueryCondition<String> ne(String tagName, String val) {
+ return new StringQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_NE, val);
+ }
+
+ /**
+ * Build a query condition for {@link String} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_MATCH} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `String match value`
+ */
+ public static PairQueryCondition<String> match(String tagName, String val) {
+ return new StringQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_MATCH, val);
+ }
+
+ /**
+ * Build a query condition for {@link String} type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_MATCH} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @param matchOption set the specific match options
+ * @return a query that `match value`
+ */
+ public static PairQueryCondition<String> match(String tagName, String val, MatchOption matchOption) {
+ return new StringQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_MATCH, val, matchOption);
+ }
+ }
+
+ /**
+ * StringArrayQueryCondition represents `tag(List of String) $op value` condition.
+ */
+ public static class StringArrayQueryCondition extends PairQueryCondition<List<String>> {
+ private StringArrayQueryCondition(String tagName, BanyandbModel.Condition.BinaryOp op, List<String> value) {
+ super(op, new TagAndValue.StringArrayTagPair(tagName, value));
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link String} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_EQ} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[String] == values`
+ */
+ public static PairQueryCondition<List<String>> eq(String tagName, List<String> val) {
+ return new StringArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_EQ, val);
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link String} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_NE} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[String] != values`
+ */
+ public static PairQueryCondition<List<String>> ne(String tagName, List<String> val) {
+ return new StringArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_NE, val);
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link String} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_HAVING} as the relation,
+ * i.e. val is a subset.
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag, a subset of the tagName's value
+ * @return a query that `[String] having values`
+ */
+ public static PairQueryCondition<List<String>> having(String tagName, List<String> val) {
+ return new StringArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_HAVING, val);
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link String} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_NOT_HAVING} as the relation,
+ * i.e. val is not a subset.
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[String] not having values`
+ */
+ public static PairQueryCondition<List<String>> notHaving(String tagName, List<String> val) {
+ return new StringArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_NOT_HAVING, val);
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link String} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_IN} as the relation,
+ * i.e. intersection of the val and the stored tagName's value must not be empty.
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[String] in values`
+ */
+ public static PairQueryCondition<List<String>> in(String tagName, List<String> val) {
+ return new StringArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_IN, val);
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link String} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_NOT_IN} as the relation,
+ * i.e. intersection of the val and the stored tagName's value must be empty.
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[String] not in values`
+ */
+ public static PairQueryCondition<List<String>> notIn(String tagName, List<String> val) {
+ return new StringArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_NOT_IN, val);
+ }
+ }
+
+ /**
+ * LongArrayQueryCondition represents `tag(List of Long) $op value` condition.
+ */
+ public static class LongArrayQueryCondition extends PairQueryCondition<List<Long>> {
+ private LongArrayQueryCondition(String tagName, BanyandbModel.Condition.BinaryOp op, List<Long> value) {
+ super(op, new TagAndValue.LongArrayTagPair(tagName, value));
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link Long} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_EQ} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[Long] == value`
+ */
+ public static PairQueryCondition<List<Long>> eq(String tagName, List<Long> val) {
+ return new LongArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_EQ, val);
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link Long} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_NE} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[Long] != value`
+ */
+ public static PairQueryCondition<List<Long>> ne(String tagName, List<Long> val) {
+ return new LongArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_NE, val);
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link Long} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_HAVING} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[Long] having values`
+ */
+ public static PairQueryCondition<List<Long>> having(String tagName, List<Long> val) {
+ return new LongArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_HAVING, val);
+ }
+
+ /**
+ * Build a query condition for {@link List} of {@link Long} as the type
+ * and {@link BanyandbModel.Condition.BinaryOp#BINARY_OP_NOT_HAVING} as the relation
+ *
+ * @param tagName name of the tag
+ * @param val value of the tag
+ * @return a query that `[Long] not having values`
+ */
+ public static PairQueryCondition<List<Long>> notHaving(String tagName, List<Long> val) {
+ return new LongArrayQueryCondition(tagName, BanyandbModel.Condition.BinaryOp.BINARY_OP_NOT_HAVING, val);
+ }
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PropertyQuery.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PropertyQuery.java
new file mode 100644
index 0000000000..73e448eb6a
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PropertyQuery.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import lombok.Setter;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+/**
+ * PropertyQuery is the high-level query API for the property model.
+ */
+@Setter
+public class PropertyQuery extends AbstractQuery<BanyandbProperty.QueryRequest> {
+ /**
+ * The limit size of the query. Default value is 20.
+ */
+ private int limit;
+
+ /**
+ * Specific property IDs to query
+ */
+ private List<String> ids;
+
+ /**
+ * Construct a property query with required fields
+ */
+ public PropertyQuery(final List<String> groups, final String name, final Set<String> projections) {
+ super(groups, name, null, projections);
+ this.limit = 20;
+ this.ids = new ArrayList<>();
+ }
+
+ /**
+ * Add a property ID to filter query results
+ * @param id property ID
+ * @return this query instance for chaining
+ */
+ public PropertyQuery id(String id) {
+ if (id != null && !id.isEmpty()) {
+ this.ids.add(id);
+ }
+ return this;
+ }
+
+ /**
+ * Add multiple property IDs to filter query results
+ * @param ids list of property IDs
+ * @return this query instance for chaining
+ */
+ public PropertyQuery ids(List<String> ids) {
+ if (ids != null) {
+ this.ids.addAll(ids);
+ }
+ return this;
+ }
+
+ @Override
+ public PropertyQuery and(PairQueryCondition<?> condition) {
+ return (PropertyQuery) super.and(condition);
+ }
+
+ @Override
+ public PropertyQuery or(PairQueryCondition<?> condition) {
+ return (PropertyQuery) super.or(condition);
+ }
+
+ public BanyandbProperty.QueryRequest build() throws BanyanDBException {
+ final BanyandbProperty.QueryRequest.Builder builder = BanyandbProperty.QueryRequest.newBuilder();
+ builder.setName(this.name);
+ builder.addAllGroups(this.groups);
+ builder.addAllTagProjection(this.tagProjections.keySet());
+ buildCriteria().ifPresent(builder::setCriteria);
+ builder.setLimit(this.limit);
+ builder.setTrace(this.trace);
+
+ if (!this.ids.isEmpty()) {
+ builder.addAllIds(this.ids);
+ }
+
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PropertyStore.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PropertyStore.java
new file mode 100644
index 0000000000..e9b9b89747
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/PropertyStore.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import io.grpc.Channel;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty.ApplyRequest;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty.ApplyRequest.Strategy;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty.ApplyResponse;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty.DeleteResponse;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty.Property;
+import org.apache.skywalking.banyandb.property.v1.PropertyServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.HandleExceptionsWith;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class PropertyStore {
+ private final PropertyServiceGrpc.PropertyServiceBlockingStub stub;
+
+ public PropertyStore(Channel channel) {
+ this.stub = PropertyServiceGrpc.newBlockingStub(channel);
+ }
+
+ public ApplyResponse apply(Property payload) throws BanyanDBException {
+ return apply(payload, Strategy.STRATEGY_MERGE);
+ }
+
+ public ApplyResponse apply(Property payload, Strategy strategy) throws BanyanDBException {
+ Strategy s = Strategy.STRATEGY_MERGE;
+ ApplyRequest r = ApplyRequest.newBuilder()
+ .setProperty(payload)
+ .setStrategy(strategy)
+ .build();
+ return HandleExceptionsWith.callAndTranslateApiException(() ->
+ this.stub.apply(r));
+ }
+
+ public DeleteResponse delete(String group, String name, String id) throws BanyanDBException {
+ return HandleExceptionsWith.callAndTranslateApiException(() ->
+ this.stub.delete(BanyandbProperty.DeleteRequest.newBuilder()
+ .setGroup(group)
+ .setName(name)
+ .setId(id)
+ .build()));
+ }
+
+ public BanyandbProperty.QueryResponse query(BanyandbProperty.QueryRequest req) throws BanyanDBException {
+ return HandleExceptionsWith.callAndTranslateApiException(() ->
+ this.stub.query(req));
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/RowEntity.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/RowEntity.java
new file mode 100644
index 0000000000..266b3e4ca4
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/RowEntity.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.protobuf.Timestamp;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+
+/**
+ * RowEntity represents an entity of BanyanDB.
+ */
+@Getter
+public class RowEntity {
+ /**
+ * timestamp of the entity in the timeunit of milliseconds.
+ */
+ protected final long timestamp;
+
+ /**
+ * tags is a map maintaining the relation between tag name and its value,
+ * (in the format of Java Types converted from gRPC Types).
+ * The family name is thus ignored, since the name should be globally unique for a schema.
+ */
+ protected final Map<String, Object> tags;
+
+ protected RowEntity(Timestamp ts, List<BanyandbModel.TagFamily> tagFamilyList) {
+ timestamp = ts.getSeconds() * 1000 + ts.getNanos() / 1_000_000;
+ this.tags = new HashMap<>();
+ for (final BanyandbModel.TagFamily tagFamily : tagFamilyList) {
+ for (final BanyandbModel.Tag tag : tagFamily.getTagsList()) {
+ final Object val = convertToJavaType(tag.getValue());
+ if (val != null) {
+ this.tags.put(tag.getKey(), val);
+ }
+ }
+ }
+ }
+
+ public <T> T getTagValue(String tagName) {
+ return (T) this.tags.get(tagName);
+ }
+
+ private Object convertToJavaType(BanyandbModel.TagValue tagValue) {
+ switch (tagValue.getValueCase()) {
+ case INT:
+ return tagValue.getInt().getValue();
+ case STR:
+ return tagValue.getStr().getValue();
+ case NULL:
+ return null;
+ case INT_ARRAY:
+ return tagValue.getIntArray().getValueList();
+ case STR_ARRAY:
+ return tagValue.getStrArray().getValueList();
+ case BINARY_DATA:
+ return tagValue.getBinaryData().toByteArray();
+ default:
+ throw new IllegalStateException("illegal type of TagValue");
+ }
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Span.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Span.java
new file mode 100644
index 0000000000..7f0b685561
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Span.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.protobuf.Timestamp;
+import java.util.List;
+import java.util.stream.Collectors;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Span represents the span of a {@link Trace}.
+ */
+@Getter
+@Setter(value = AccessLevel.PRIVATE)
+public class Span {
+ private Timestamp startTime;
+ private Timestamp endTime;
+ private boolean error;
+ private List<Tag> tags;
+ private String message;
+ private List<Span> children;
+ private long duration;
+
+ static Span convertSpanFromProto(org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Span protoSpan) {
+ Span spanBean = new Span();
+ spanBean.setStartTime(protoSpan.getStartTime());
+ spanBean.setEndTime(protoSpan.getEndTime());
+ spanBean.setError(protoSpan.getError());
+ spanBean.setMessage(protoSpan.getMessage());
+ spanBean.setDuration(protoSpan.getDuration());
+ spanBean.setTags(protoSpan.getTagsList().stream()
+ .map(Tag::convertTagFromProto)
+ .collect(Collectors.toList()));
+ spanBean.setChildren(protoSpan.getChildrenList().stream()
+ .map(Span::convertSpanFromProto)
+ .collect(Collectors.toList()));
+ return spanBean;
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamQuery.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamQuery.java
new file mode 100644
index 0000000000..8e0950482d
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamQuery.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import lombok.Setter;
+import org.apache.skywalking.banyandb.stream.v1.BanyandbStream;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+/**
+ * StreamQuery is the high-level query API for the stream model.
+ */
+@Setter
+public class StreamQuery extends AbstractQuery<BanyandbStream.QueryRequest> {
+ /**
+ * The starting row id of the query. Default value is 0.
+ */
+ private int offset;
+ /**
+ * The limit size of the query. Default value is 20.
+ */
+ private int limit;
+ /**
+ * One order condition is supported and optional.
+ */
+ private OrderBy orderBy;
+
+ /**
+ * The stages of the stream query.
+ */
+ private Set<String> stages;
+
+ public StreamQuery(final List<String> groups,
+ final String name,
+ final TimestampRange timestampRange,
+ final Map<String/*tagName*/, String/*tagFamilyName*/> projections) {
+ super(groups, name, timestampRange, projections);
+ this.offset = 0;
+ this.limit = 20;
+ }
+
+ public StreamQuery(final List<String> groups,
+ final String name,
+ final Map<String/*tagName*/, String/*tagFamilyName*/> projections) {
+ this(groups, name, null, projections);
+ }
+
+ @Override
+ public StreamQuery and(PairQueryCondition<?> condition) {
+ return (StreamQuery) super.and(condition);
+ }
+
+ @Override
+ public StreamQuery or(PairQueryCondition<?> condition) {
+ return (StreamQuery) super.or(condition);
+ }
+
+ public StreamQuery stages(Set<String> stages) {
+ this.stages = stages;
+ return this;
+ }
+
+ @Override
+ BanyandbStream.QueryRequest build() throws BanyanDBException {
+ final BanyandbStream.QueryRequest.Builder builder = BanyandbStream.QueryRequest.newBuilder();
+ builder.setName(this.name);
+ builder.addAllGroups(this.groups);
+ if (timestampRange != null) {
+ builder.setTimeRange(timestampRange.build());
+ }
+ builder.setProjection(buildTagProjections());
+ buildCriteria().ifPresent(builder::setCriteria);
+ builder.setOffset(offset);
+ builder.setLimit(limit);
+ if (orderBy != null) {
+ builder.setOrderBy(orderBy.build());
+ }
+ if (stages != null && !stages.isEmpty()) {
+ builder.addAllStages(stages);
+ }
+ builder.setTrace(this.trace);
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamQueryResponse.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamQueryResponse.java
new file mode 100644
index 0000000000..adea6f6a07
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamQueryResponse.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.stream.v1.BanyandbStream;
+
+/**
+ * StreamQueryResponse represents the stream query result.
+ */
+public class StreamQueryResponse {
+ @Getter
+ private final List<Element> elements;
+
+ @Getter
+ private final Trace trace;
+
+ StreamQueryResponse(BanyandbStream.QueryResponse response) {
+ final List<BanyandbStream.Element> elementsList = response.getElementsList();
+ elements = new ArrayList<>(elementsList.size());
+ elementsList.forEach(element -> elements.add(Element.create(element)));
+ this.trace = Trace.convertFromProto(response.getTrace());
+ }
+
+ /**
+ * @return size of the response set.
+ */
+ public int size() {
+ return elements.size();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamWrite.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamWrite.java
new file mode 100644
index 0000000000..91f80c0b1b
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/StreamWrite.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.protobuf.Timestamp;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.banyandb.stream.v1.BanyandbStream;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.Serializable;
+
+/**
+ * StreamWrite represents a write operation, including necessary fields.
+ */
+public class StreamWrite extends AbstractWrite<BanyandbStream.WriteRequest> {
+ /**
+ * ID of current entity
+ */
+ @Getter
+ private final String elementId;
+
+ // Use TreeMap to have a consistent order for tags
+ private final Map<String/*tagFamily*/, Map<String/*tagName*/, Serializable<BanyandbModel.TagValue>/*tagValue*/>> tags;
+
+ /**
+ * Create a StreamWrite without initial timestamp.
+ */
+ StreamWrite(BanyandbCommon.Metadata entityMetadata, final String elementId) {
+ super(entityMetadata);
+ this.elementId = elementId;
+ this.tags = new TreeMap<>();
+ }
+
+ public StreamWrite tag(String tagFamilyName, String tagName, Serializable<BanyandbModel.TagValue> tagValue) throws BanyanDBException {
+ this.tags.computeIfAbsent(tagFamilyName, k -> new TreeMap<>()).put(tagName, tagValue);
+ return this;
+ }
+
+ public void setTimestamp(long timestamp) {
+ super.timestamp = Optional.of(timestamp);
+ }
+
+ /**
+ * Build a write request
+ *
+ * @return {@link BanyandbStream.WriteRequest} for the bulk process.
+ */
+ @Override
+ protected BanyandbStream.WriteRequest build(BanyandbCommon.Metadata metadata) {
+ if (!timestamp.isPresent() || timestamp.get() <= 0) {
+ throw new IllegalArgumentException("Timestamp is required and must be greater than 0 for stream writes.");
+ }
+
+ Timestamp ts = Timestamp.newBuilder()
+ .setSeconds(timestamp.get() / 1000)
+ .setNanos((int) (timestamp.get() % 1000 * 1_000_000)).build();
+
+ final BanyandbStream.WriteRequest.Builder builder = BanyandbStream.WriteRequest.newBuilder();
+ builder.setMetadata(metadata);
+ final BanyandbStream.ElementValue.Builder elemValBuilder = BanyandbStream.ElementValue.newBuilder();
+ elemValBuilder.setElementId(elementId);
+ elemValBuilder.setTimestamp(ts);
+ for (Map.Entry<String, Map<String, Serializable<BanyandbModel.TagValue>>> tagFamilyEntry : this.tags.entrySet()) {
+ BanyandbStream.TagFamilySpec.Builder tagFamilySpecBuilder = BanyandbStream.TagFamilySpec.newBuilder();
+ BanyandbModel.TagFamilyForWrite.Builder tagFamilyForWriteBuilder = BanyandbModel.TagFamilyForWrite.newBuilder();
+ tagFamilySpecBuilder.setName(tagFamilyEntry.getKey());
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : tagFamilyEntry.getValue().entrySet()) {
+ tagFamilySpecBuilder.addTagNames(tagEntry.getKey());
+ tagFamilyForWriteBuilder.addTags(tagEntry.getValue().serialize());
+ }
+ builder.addTagFamilySpec(tagFamilySpecBuilder);
+ elemValBuilder.addTagFamilies(tagFamilyForWriteBuilder);
+ }
+
+ builder.setElement(elemValBuilder);
+ builder.setMessageId(System.nanoTime());
+ return builder.build();
+ }
+
+ /**
+ * Build a write request without metadata and tag specs.
+ *
+ * @return {@link BanyandbStream.WriteRequest} for the bulk process.
+ */
+ @Override
+ protected BanyandbStream.WriteRequest buildValues() {
+ if (!timestamp.isPresent() || timestamp.get() <= 0) {
+ throw new IllegalArgumentException("Timestamp is required and must be greater than 0 for stream writes.");
+ }
+
+ Timestamp ts = Timestamp.newBuilder()
+ .setSeconds(timestamp.get() / 1000)
+ .setNanos((int) (timestamp.get() % 1000 * 1_000_000)).build();
+
+ final BanyandbStream.WriteRequest.Builder builder = BanyandbStream.WriteRequest.newBuilder();
+ final BanyandbStream.ElementValue.Builder elemValBuilder = BanyandbStream.ElementValue.newBuilder();
+ elemValBuilder.setElementId(elementId);
+ elemValBuilder.setTimestamp(ts);
+ for (Map.Entry<String, Map<String, Serializable<BanyandbModel.TagValue>>> tagFamilyEntry : this.tags.entrySet()) {
+ BanyandbModel.TagFamilyForWrite.Builder tagFamilyForWriteBuilder = BanyandbModel.TagFamilyForWrite.newBuilder();
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : tagFamilyEntry.getValue().entrySet()) {
+ tagFamilyForWriteBuilder.addTags(tagEntry.getValue().serialize());
+ }
+ elemValBuilder.addTagFamilies(tagFamilyForWriteBuilder);
+ }
+
+ builder.setElement(elemValBuilder);
+ builder.setMessageId(System.nanoTime());
+ return builder.build();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("group=").append(entityMetadata.getGroup()).append(", ").append("name=")
+ .append(entityMetadata.getName()).append(", ").append("timestamp=").append(timestamp).append(", ");
+ for (Map.Entry<String, Map<String, Serializable<BanyandbModel.TagValue>>> entry : tags.entrySet()) {
+ String tagFamilyName = entry.getKey();
+ Map<String, Serializable<BanyandbModel.TagValue>> tagMap = entry.getValue();
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : tagMap.entrySet()) {
+ String tagName = tagEntry.getKey();
+ Serializable<BanyandbModel.TagValue> tagValue = tagEntry.getValue();
+ stringBuilder.append(tagFamilyName).append(".").append(tagName).append("=")
+ .append(tagValue.serialize()).append(", ");
+ }
+ }
+ return stringBuilder.toString();
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Tag.java
similarity index 59%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Tag.java
index dbef0e0068..2b41121e99 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Tag.java
@@ -16,20 +16,28 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client;
+import lombok.AccessLevel;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import lombok.Setter;
+import lombok.ToString;
-@RequiredArgsConstructor
+/**
+ * Tag represents the key-value pair of a tag in a {@link Span}.
+ */
@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+@Setter(value = AccessLevel.PRIVATE)
+@ToString
+public class Tag {
+ private String key;
+ private String value;
+ static Tag convertTagFromProto(org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Tag protoTag) {
+ Tag tagBean = new Tag();
+ tagBean.setKey(protoTag.getKey());
+ tagBean.setValue(protoTag.getValue());
+ return tagBean;
}
-}
\ No newline at end of file
+}
+
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TagAndValue.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TagAndValue.java
new file mode 100644
index 0000000000..73c79fd3c0
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TagAndValue.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.NullValue;
+import com.google.protobuf.Timestamp;
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+
+/**
+ * TagAndValue represents a value of column in the response
+ */
+@EqualsAndHashCode(callSuper = true)
+public abstract class TagAndValue<T> extends Value<T> {
+ protected final String tagName;
+
+ protected TagAndValue(String tagName, T value) {
+ super(value);
+ this.tagName = tagName;
+ }
+
+ /**
+ * @return tag name
+ */
+ public String getTagName() {
+ return this.tagName;
+ }
+
+ /**
+ * @return true if value is null;
+ */
+ public boolean isNull() {
+ return this.value == null;
+ }
+
+ /**
+ * TagValue is referenced from various derived data structs.
+ *
+ * @return TagValue to be included
+ */
+ protected abstract BanyandbModel.TagValue buildTypedTagValue();
+
+ public BanyandbModel.Tag build() {
+ return BanyandbModel.Tag.newBuilder()
+ .setKey(this.tagName)
+ .setValue(buildTypedTagValue())
+ .build();
+ }
+
+ public static TagAndValue<?> fromProtobuf(BanyandbModel.Tag tag) {
+ switch (tag.getValue().getValueCase()) {
+ case INT:
+ return new LongTagPair(tag.getKey(), tag.getValue().getInt().getValue());
+ case STR:
+ return new StringTagPair(tag.getKey(), tag.getValue().getStr().getValue());
+ case INT_ARRAY:
+ return new LongArrayTagPair(tag.getKey(), tag.getValue().getIntArray().getValueList());
+ case STR_ARRAY:
+ return new StringArrayTagPair(tag.getKey(), tag.getValue().getStrArray().getValueList());
+ case BINARY_DATA:
+ return new BinaryTagPair(tag.getKey(), tag.getValue().getBinaryData());
+ case TIMESTAMP:
+ return new TimestampTagPair(tag.getKey(), tag.getValue().getTimestamp());
+ case NULL:
+ return new NullTagPair(tag.getKey());
+ default:
+ throw new IllegalArgumentException("Unrecognized NullType");
+ }
+ }
+
+ @EqualsAndHashCode(callSuper = true)
+ public static class StringTagPair extends TagAndValue<String> {
+ StringTagPair(final String tagName, final String value) {
+ super(tagName, value);
+ }
+
+ @Override
+ protected BanyandbModel.TagValue buildTypedTagValue() {
+ return BanyandbModel.TagValue.newBuilder()
+ .setStr(BanyandbModel.Str
+ .newBuilder()
+ .setValue(value).build())
+ .build();
+ }
+ }
+
+ public static TagAndValue<String> newStringTag(final String tagName, final String value) {
+ return new StringTagPair(tagName, value);
+ }
+
+ @EqualsAndHashCode(callSuper = true)
+ public static class StringArrayTagPair extends TagAndValue<List<String>> {
+ StringArrayTagPair(final String tagName, final List<String> value) {
+ super(tagName, value);
+ }
+
+ @Override
+ protected BanyandbModel.TagValue buildTypedTagValue() {
+ return BanyandbModel.TagValue.newBuilder()
+ .setStrArray(BanyandbModel.StrArray
+ .newBuilder()
+ .addAllValue(value).build())
+ .build();
+ }
+ }
+
+ public static TagAndValue<List<String>> newStringArrayTagPair(final String tagName, final List<String> value) {
+ return new StringArrayTagPair(tagName, value);
+ }
+
+ @EqualsAndHashCode(callSuper = true)
+ public static class LongTagPair extends TagAndValue<Long> {
+ LongTagPair(final String tagName, final Long value) {
+ super(tagName, value);
+ }
+
+ @Override
+ protected BanyandbModel.TagValue buildTypedTagValue() {
+ return BanyandbModel.TagValue.newBuilder()
+ .setInt(BanyandbModel.Int
+ .newBuilder()
+ .setValue(value).build())
+ .build();
+ }
+ }
+
+ public static TagAndValue<Long> newLongTag(final String tagName, final long value) {
+ return new LongTagPair(tagName, value);
+ }
+
+ @EqualsAndHashCode(callSuper = true)
+ public static class LongArrayTagPair extends TagAndValue<List<Long>> {
+ LongArrayTagPair(final String tagName, final List<Long> value) {
+ super(tagName, value);
+ }
+
+ @Override
+ protected BanyandbModel.TagValue buildTypedTagValue() {
+ return BanyandbModel.TagValue.newBuilder()
+ .setIntArray(BanyandbModel.IntArray
+ .newBuilder()
+ .addAllValue(value).build())
+ .build();
+ }
+ }
+
+ public static TagAndValue<Long> newLongArrayTag(final String tagName, final long value) {
+ return new LongTagPair(tagName, value);
+ }
+
+ @EqualsAndHashCode(callSuper = true)
+ public static class BinaryTagPair extends TagAndValue<ByteString> {
+ public BinaryTagPair(String fieldName, ByteString byteString) {
+ super(fieldName, byteString);
+ }
+
+ @Override
+ protected BanyandbModel.TagValue buildTypedTagValue() {
+ return BanyandbModel.TagValue.newBuilder()
+ .setBinaryData(this.value)
+ .build();
+ }
+ }
+
+ public static TagAndValue<ByteString> newBinaryTag(final String tagName, final byte[] bytes) {
+ return new BinaryTagPair(tagName, ByteString.copyFrom(bytes));
+ }
+
+ @EqualsAndHashCode(callSuper = true)
+ public static class TimestampTagPair extends TagAndValue<Long> {
+ TimestampTagPair(final String tagName, final long epochMilli) {
+ super(tagName, epochMilli);
+ }
+
+ TimestampTagPair(final String tagName, final Timestamp value) {
+ super(tagName, value.getSeconds() * 1000 + value.getNanos() / 1000000);
+ }
+
+ @Override
+ protected BanyandbModel.TagValue buildTypedTagValue() {
+ Timestamp timestamp = Timestamp.newBuilder()
+ .setSeconds(value / 1000)
+ .setNanos((int) ((value % 1000) * 1000000))
+ .build();
+ return BanyandbModel.TagValue.newBuilder()
+ .setTimestamp(timestamp)
+ .build();
+ }
+ }
+
+ public static TagAndValue<Long> newTimestampTag(final String tagName, final long epochMilli) {
+ return new TimestampTagPair(tagName, epochMilli);
+ }
+
+ @EqualsAndHashCode(callSuper = true)
+ public static class NullTagPair extends TagAndValue<Void> {
+ NullTagPair(final String tagName) {
+ super(tagName, null);
+ }
+
+ @Override
+ protected BanyandbModel.TagValue buildTypedTagValue() {
+ return BanyandbModel.TagValue.newBuilder()
+ .setNull(NullValue.NULL_VALUE)
+ .build();
+ }
+
+ @Override
+ public boolean isNull() {
+ return true;
+ }
+ }
+
+ public static TagAndValue<Void> newNullTag(final String tagName) {
+ return new NullTagPair(tagName);
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TimestampRange.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TimestampRange.java
new file mode 100644
index 0000000000..1bfc9012da
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TimestampRange.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.protobuf.Timestamp;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.ToString;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.library.banyandb.v1.client.util.TimeUtils;
+
+@RequiredArgsConstructor
+@Getter
+@ToString
+public class TimestampRange {
+ static final BanyandbModel.TimeRange MAX_RANGE = BanyandbModel.TimeRange.newBuilder()
+ .setBegin(TimeUtils.fromEpochNanos(Long.MIN_VALUE))
+ .setEnd(TimeUtils.fromEpochNanos(Long.MAX_VALUE))
+ .build();
+ /**
+ * start timestamp in timeunit of milliseconds. inclusive.
+ */
+ private final long begin;
+
+ /**
+ * end timestamp in timeunit of milliseconds. inclusive.
+ */
+ private final long end;
+
+ /**
+ * @return TimeRange accordingly.
+ */
+ BanyandbModel.TimeRange build() {
+ final BanyandbModel.TimeRange.Builder builder = BanyandbModel.TimeRange.newBuilder();
+ builder.setBegin(Timestamp.newBuilder()
+ .setSeconds(begin / 1000)
+ .setNanos((int) (begin % 1000 * 1_000_000)));
+ builder.setEnd(Timestamp.newBuilder()
+ .setSeconds(end / 1000)
+ .setNanos((int) (end % 1000 * 1_000_000)));
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TopNQuery.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TopNQuery.java
new file mode 100644
index 0000000000..7bb4a386ce
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TopNQuery.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.common.base.Preconditions;
+import java.util.List;
+import lombok.Setter;
+import org.apache.skywalking.banyandb.measure.v1.BanyandbMeasure;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+@Setter
+public class TopNQuery {
+ private final List<String> groups;
+ private final String name;
+ private final TimestampRange timestampRange;
+ private final int number;
+ private final AbstractQuery.Sort sort;
+ private MeasureQuery.Aggregation.Type aggregationType = MeasureQuery.Aggregation.Type.UNSPECIFIED;
+ private List<String> stages;
+ /**
+ * Query conditions.
+ */
+ private List<PairQueryCondition<?>> conditions;
+
+ public TopNQuery(List<String> groups, String name, TimestampRange timestampRange, int number, AbstractQuery.Sort sort) {
+ Preconditions.checkArgument(sort != AbstractQuery.Sort.UNSPECIFIED);
+ Preconditions.checkArgument(number > 0);
+ this.groups = groups;
+ this.name = name;
+ this.timestampRange = timestampRange;
+ this.number = number;
+ this.sort = sort;
+ }
+
+ BanyandbMeasure.TopNRequest build() throws BanyanDBException {
+ BanyandbMeasure.TopNRequest.Builder bld = BanyandbMeasure.TopNRequest.newBuilder()
+ .setName(name)
+ .addAllGroups(groups)
+ .setTimeRange(timestampRange.build())
+ .setTopN(number)
+ .setFieldValueSort(AbstractQuery.Sort.DESC == sort ? BanyandbModel.Sort.SORT_DESC : BanyandbModel.Sort.SORT_ASC);
+ if (aggregationType == null) {
+ bld.setAgg(BanyandbModel.AggregationFunction.AGGREGATION_FUNCTION_UNSPECIFIED);
+ } else {
+ bld.setAgg(aggregationType.function);
+ }
+ if (conditions != null && !conditions.isEmpty()) {
+ for (final PairQueryCondition<?> expr : conditions) {
+ if (expr.op != BanyandbModel.Condition.BinaryOp.BINARY_OP_EQ) {
+ throw new UnsupportedOperationException("only equality is supported");
+ }
+ bld.addConditions(expr.build().getCondition());
+ }
+ }
+ if (stages != null && !stages.isEmpty()) {
+ for (String stage : stages) {
+ bld.addStages(stage);
+ }
+ }
+ return bld.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TopNQueryResponse.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TopNQueryResponse.java
new file mode 100644
index 0000000000..a7d5bc5442
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TopNQueryResponse.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.protobuf.Timestamp;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.skywalking.banyandb.measure.v1.BanyandbMeasure;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+
+public class TopNQueryResponse {
+ @Getter
+ private final List<TopNList> topNLists;
+
+ TopNQueryResponse(BanyandbMeasure.TopNResponse response) {
+ final List<BanyandbMeasure.TopNList> timelines = response.getListsList();
+ topNLists = new ArrayList<>(timelines.size());
+ for (final BanyandbMeasure.TopNList topNList : timelines) {
+ topNLists.add(new TopNList(topNList.getTimestamp(), topNList.getItemsList()));
+ }
+ }
+
+ public int size() {
+ return this.topNLists == null ? 0 : this.topNLists.size();
+ }
+
+ @Getter
+ public static class TopNList {
+ /**
+ * timestamp of the entity in the timeunit of milliseconds.
+ */
+ private final long timestamp;
+ private final List<Item> items;
+
+ private TopNList(Timestamp ts, List<BanyandbMeasure.TopNList.Item> itemsList) {
+ this.timestamp = ts.getSeconds() * 1000 + ts.getNanos() / 1_000_000;
+ this.items = new ArrayList<>(itemsList.size());
+ for (final BanyandbMeasure.TopNList.Item item : itemsList) {
+ this.items.add(Item.parseFrom(item));
+ }
+ }
+ }
+
+ @RequiredArgsConstructor
+ @Getter
+ public static class Item {
+ private final Map<String, TagAndValue<?>> tagValuesMap;
+ private final Object value;
+
+ static Item parseFrom(BanyandbMeasure.TopNList.Item item) {
+ final Object fieldValue = DataPoint.convertFileValueToJavaType(item.getValue());
+ final Map<String, TagAndValue<?>> map = new HashMap<>(item.getEntityCount());
+ for (final BanyandbModel.Tag tag : item.getEntityList()) {
+ map.put(tag.getKey(), TagAndValue.fromProtobuf(tag));
+ }
+ return new Item(map, fieldValue);
+ }
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMeasureUpdateRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Trace.java
similarity index 51%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMeasureUpdateRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Trace.java
index 763537a034..efedee8c3c 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMeasureUpdateRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Trace.java
@@ -16,23 +16,31 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.measure;
+package org.apache.skywalking.library.banyandb.v1.client;
+import java.util.List;
+import java.util.stream.Collectors;
+import lombok.AccessLevel;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.MeasureWrite;
-import org.apache.skywalking.oap.server.core.UnexpectedException;
-import org.apache.skywalking.oap.server.library.client.request.UpdateRequest;
+import lombok.Setter;
-@RequiredArgsConstructor
+/**
+ * Trace represents the trace of a request.
+ */
@Getter
-public class BanyanDBMeasureUpdateRequest implements UpdateRequest {
- private final MeasureWrite measureWrite;
+@Setter(value = AccessLevel.PRIVATE)
+public class Trace {
+ private String traceId;
+ private List<Span> spans;
+ private boolean error;
- @Override
- public void onUpdateFailure() {
- // BanyanDB measure update is equivalent to insert.
- // If something goes wrong, then it is a code bug or server-side is not available
- throw new UnexpectedException("Should not report onUpdateFailure when measure update.");
+ static Trace convertFromProto(org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Trace protoTrace) {
+ Trace traceBean = new Trace();
+ traceBean.setTraceId(protoTrace.getTraceId());
+ traceBean.setError(protoTrace.getError());
+ traceBean.setSpans(protoTrace.getSpansList().stream()
+ .map(Span::convertSpanFromProto)
+ .collect(Collectors.toList()));
+ return traceBean;
}
}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceQuery.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceQuery.java
new file mode 100644
index 0000000000..5e6cadc12e
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceQuery.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.List;
+import java.util.Set;
+import lombok.Setter;
+import org.apache.skywalking.banyandb.trace.v1.BanyandbTrace;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+/**
+ * TraceQuery is the high-level query API for the trace model.
+ */
+@Setter
+public class TraceQuery extends AbstractQuery<BanyandbTrace.QueryRequest> {
+ /**
+ * The starting row id of the query. Default value is 0.
+ */
+ private int offset;
+ /**
+ * The limit size of the query. Default value is 20.
+ */
+ private int limit;
+ /**
+ * One order condition is supported and optional.
+ */
+ private OrderBy orderBy;
+
+ /**
+ * The stages of the trace query.
+ */
+ private Set<String> stages;
+
+ public TraceQuery(final List<String> groups, final String name, final TimestampRange timestampRange, final Set<String> projections) {
+ super(groups, name, timestampRange, projections);
+ this.offset = 0;
+ this.limit = 20;
+ }
+
+ public TraceQuery(final List<String> groups, final String name, final Set<String> projections) {
+ this(groups, name, null, projections);
+ }
+
+ @Override
+ public TraceQuery and(PairQueryCondition<?> condition) {
+ return (TraceQuery) super.and(condition);
+ }
+
+ @Override
+ public TraceQuery or(PairQueryCondition<?> condition) {
+ return (TraceQuery) super.or(condition);
+ }
+
+ public TraceQuery stages(Set<String> stages) {
+ this.stages = stages;
+ return this;
+ }
+
+ @Override
+ BanyandbTrace.QueryRequest build() throws BanyanDBException {
+ final BanyandbTrace.QueryRequest.Builder builder = BanyandbTrace.QueryRequest.newBuilder();
+ builder.setName(this.name);
+ builder.addAllGroups(this.groups);
+ if (timestampRange != null) {
+ builder.setTimeRange(timestampRange.build());
+ }
+ builder.addAllTagProjection(tagProjections.keySet());
+ buildCriteria().ifPresent(builder::setCriteria);
+ builder.setOffset(offset);
+ builder.setLimit(limit);
+ if (orderBy != null) {
+ builder.setOrderBy(orderBy.build());
+ }
+ if (stages != null && !stages.isEmpty()) {
+ builder.addAllStages(stages);
+ }
+ builder.setTrace(this.trace);
+ return builder.build();
+ }
+}
\ No newline at end of file
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceQueryResponse.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceQueryResponse.java
new file mode 100644
index 0000000000..b18e9a4a3e
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceQueryResponse.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.util.List;
+import org.apache.skywalking.banyandb.trace.v1.BanyandbTrace;
+
+/**
+ * TraceQueryResponse is a high-level response object for the trace query API.
+ */
+public class TraceQueryResponse {
+ private final BanyandbTrace.QueryResponse response;
+ private final Trace trace;
+
+ TraceQueryResponse(BanyandbTrace.QueryResponse response) {
+ this.response = response;
+ this.trace = Trace.convertFromProto(response.getTraceQueryResult());
+ }
+
+ /**
+ * Get the list of traces returned by the query.
+ *
+ * @return list of traces, each containing spans grouped by trace ID
+ */
+ public List<BanyandbTrace.Trace> getTraces() {
+ return response.getTracesList();
+ }
+
+ /**
+ * Get the trace query execution trace if enabled.
+ */
+ public Trace getTraceResult() {
+ return this.trace;
+ }
+
+ /**
+ * Get the total number of traces returned.
+ *
+ * @return trace count
+ */
+ public int size() {
+ return response.getTracesCount();
+ }
+
+ /**
+ * Check if the response is empty.
+ *
+ * @return true if no traces were returned
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+}
\ No newline at end of file
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceWrite.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceWrite.java
new file mode 100644
index 0000000000..45c0bc31a8
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/TraceWrite.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.protobuf.ByteString;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.banyandb.trace.v1.BanyandbTrace;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.Serializable;
+
+/**
+ * TraceWrite represents a write operation, including necessary fields.
+ */
+public class TraceWrite extends AbstractWrite<BanyandbTrace.WriteRequest> {
+ /**
+ * Span data in binary format
+ */
+ @Getter
+ private ByteString span;
+
+ /**
+ * Version for write request
+ */
+ @Getter
+ private long version;
+
+ private final Map<String/*tagName*/, Serializable<BanyandbModel.TagValue>/*tagValue*/> tags;
+
+ /**
+ * Create a TraceWrite without initial timestamp.
+ */
+ TraceWrite(BanyandbCommon.Metadata entityMetadata) {
+ super(entityMetadata);
+ this.span = ByteString.EMPTY;
+ this.version = 1L;
+ this.tags = new TreeMap<>();
+ }
+
+ public TraceWrite tag(String tagName, Serializable<BanyandbModel.TagValue> tagValue) throws BanyanDBException {
+ this.tags.put(tagName, tagValue);
+ return this;
+ }
+
+ /**
+ * Set span data
+ *
+ * @param span span data in bytes
+ */
+ public TraceWrite span(byte[] span) {
+ this.span = ByteString.copyFrom(span);
+ return this;
+ }
+
+ /**
+ * Set span data
+ *
+ * @param span span data as ByteString
+ */
+ public TraceWrite span(ByteString span) {
+ this.span = span;
+ return this;
+ }
+
+ /**
+ * Set version
+ *
+ * @param version write request version
+ */
+ public TraceWrite version(long version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Build a write request
+ *
+ * @return {@link BanyandbTrace.WriteRequest} for the bulk process.
+ */
+ @Override
+ protected BanyandbTrace.WriteRequest build(BanyandbCommon.Metadata metadata) {
+ final BanyandbTrace.WriteRequest.Builder builder = BanyandbTrace.WriteRequest.newBuilder();
+ builder.setMetadata(metadata);
+ BanyandbTrace.TagSpec.Builder tagSpecBuilder = BanyandbTrace.TagSpec.newBuilder();
+
+ List<BanyandbModel.TagValue> tagValues = new ArrayList<>();
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : this.tags.entrySet()) {
+ tagSpecBuilder.addTagNames(tagEntry.getKey());
+ tagValues.add(tagEntry.getValue().serialize());
+ }
+
+ builder.setTagSpec(tagSpecBuilder);
+ builder.addAllTags(tagValues);
+ builder.setSpan(this.span);
+ builder.setVersion(this.version);
+ return builder.build();
+ }
+
+ /**
+ * Build a write request without metadata and tag specs.
+ *
+ * @return {@link BanyandbTrace.WriteRequest} for the bulk process.
+ */
+ @Override
+ protected BanyandbTrace.WriteRequest buildValues() {
+ final BanyandbTrace.WriteRequest.Builder builder = BanyandbTrace.WriteRequest.newBuilder();
+
+ List<BanyandbModel.TagValue> tagValues = new ArrayList<>();
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : this.tags.entrySet()) {
+ tagValues.add(tagEntry.getValue().serialize());
+ }
+
+ builder.addAllTags(tagValues);
+ builder.setSpan(this.span);
+ builder.setVersion(this.version);
+ return builder.build();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("group=").append(entityMetadata.getGroup()).append(", ").append("name=")
+ .append(entityMetadata.getName()).append(", ").append("timestamp=").append(timestamp).append(", ");
+ for (Map.Entry<String, Serializable<BanyandbModel.TagValue>> tagEntry : tags.entrySet()) {
+ String tagName = tagEntry.getKey();
+ Serializable<BanyandbModel.TagValue> tagValue = tagEntry.getValue();
+ stringBuilder.append(tagName).append("=")
+ .append(tagValue.serialize()).append(", ");
+ }
+ return stringBuilder.toString();
+ }
+}
\ No newline at end of file
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Value.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Value.java
new file mode 100644
index 0000000000..667f716925
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/Value.java
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.common.base.Strings;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.Timestamp;
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.Serializable;
+
+import static com.google.protobuf.NullValue.NULL_VALUE;
+
+/**
+ * Field represents a value in the write-op or response.
+ */
+@EqualsAndHashCode
+public abstract class Value<T> {
+ @Getter
+ protected final T value;
+
+ protected Value(T value) {
+ this.value = value;
+ }
+
+ /**
+ * NullTagValue is a value which can be converted to {@link com.google.protobuf.NullValue}.
+ * Users should use the singleton instead of create a new instance everytime.
+ */
+ public static class NullTagValue extends Value<Object> implements Serializable<BanyandbModel.TagValue> {
+ private static final NullTagValue INSTANCE = new NullTagValue();
+
+ private NullTagValue() {
+ super(null);
+ }
+
+ @Override
+ public BanyandbModel.TagValue serialize() {
+ return BanyandbModel.TagValue.newBuilder().setNull(NULL_VALUE).build();
+ }
+ }
+
+ /**
+ * The value of a String type tag.
+ */
+ public static class StringTagValue extends Value<String> implements Serializable<BanyandbModel.TagValue> {
+ private StringTagValue(String value) {
+ super(value);
+ }
+
+ @Override
+ public BanyandbModel.TagValue serialize() {
+ return BanyandbModel.TagValue.newBuilder().setStr(BanyandbModel.Str.newBuilder().setValue(value)).build();
+ }
+ }
+
+ /**
+ * The value of a String array type tag.
+ */
+ public static class StringArrayTagValue extends Value<List<String>> implements Serializable<BanyandbModel.TagValue> {
+ private StringArrayTagValue(List<String> value) {
+ super(value);
+ }
+
+ @Override
+ public BanyandbModel.TagValue serialize() {
+ return BanyandbModel.TagValue.newBuilder().setStrArray(BanyandbModel.StrArray.newBuilder().addAllValue(value)).build();
+ }
+ }
+
+ /**
+ * The value of an int64(Long) type tag.
+ */
+ public static class LongTagValue extends Value<Long> implements Serializable<BanyandbModel.TagValue> {
+ private LongTagValue(Long value) {
+ super(value);
+ }
+
+ @Override
+ public BanyandbModel.TagValue serialize() {
+ return BanyandbModel.TagValue.newBuilder().setInt(BanyandbModel.Int.newBuilder().setValue(value)).build();
+ }
+ }
+
+ /**
+ * The value of an int64(Long) array type tag.
+ */
+ public static class LongArrayTagValue extends Value<List<Long>> implements Serializable<BanyandbModel.TagValue> {
+ private LongArrayTagValue(List<Long> value) {
+ super(value);
+ }
+
+ @Override
+ public BanyandbModel.TagValue serialize() {
+ return BanyandbModel.TagValue.newBuilder().setIntArray(BanyandbModel.IntArray.newBuilder().addAllValue(value)).build();
+ }
+ }
+
+ /**
+ * The value of a byte array(ByteString) type tag.
+ */
+ public static class BinaryTagValue extends Value<ByteString> implements Serializable<BanyandbModel.TagValue> {
+ public BinaryTagValue(ByteString byteString) {
+ super(byteString);
+ }
+
+ @Override
+ public BanyandbModel.TagValue serialize() {
+ return BanyandbModel.TagValue.newBuilder().setBinaryData(value).build();
+ }
+ }
+
+ /**
+ * The value of a timestamp type tag.
+ */
+ public static class TimestampTagValue extends Value<Long> implements Serializable<BanyandbModel.TagValue> {
+ private TimestampTagValue(Long value) {
+ super(value);
+ }
+
+ @Override
+ public BanyandbModel.TagValue serialize() {
+ Timestamp timestamp = toProtobufTimestamp(value);
+ return BanyandbModel.TagValue.newBuilder().setTimestamp(timestamp).build();
+ }
+ }
+
+ /**
+ * Utility method to convert milliseconds to protobuf Timestamp.
+ */
+ private static Timestamp toProtobufTimestamp(long millis) {
+ return Timestamp.newBuilder()
+ .setSeconds(millis / 1000)
+ .setNanos((int) ((millis % 1000) * 1000000))
+ .build();
+ }
+
+ /**
+ * Construct a string tag
+ *
+ * @param val payload
+ * @return Anonymous tag with String payload
+ */
+ public static Serializable<BanyandbModel.TagValue> stringTagValue(String val) {
+ if (val == null) {
+ return nullTagValue();
+ }
+ return new StringTagValue(val);
+ }
+
+ /**
+ * Construct a numeric tag
+ *
+ * @param val payload
+ * @return Anonymous tag with numeric payload
+ */
+ public static Serializable<BanyandbModel.TagValue> longTagValue(Long val) {
+ if (val == null) {
+ return nullTagValue();
+ }
+ return new LongTagValue(val);
+ }
+
+ /**
+ * Construct a string array tag
+ *
+ * @param val payload
+ * @return Anonymous tag with string array payload
+ */
+ public static Serializable<BanyandbModel.TagValue> stringArrayTagValue(List<String> val) {
+ if (val == null) {
+ return nullTagValue();
+ }
+ return new StringArrayTagValue(val);
+ }
+
+ /**
+ * Construct a byte array tag.
+ *
+ * @param bytes binary data
+ * @return Anonymous tag with binary payload
+ */
+ public static Serializable<BanyandbModel.TagValue> binaryTagValue(byte[] bytes) {
+ if (bytes == null) {
+ return nullTagValue();
+ }
+ return new BinaryTagValue(ByteString.copyFrom(bytes));
+ }
+
+ /**
+ * Construct a long array tag
+ *
+ * @param val payload
+ * @return Anonymous tag with numeric array payload
+ */
+ public static Serializable<BanyandbModel.TagValue> longArrayTag(List<Long> val) {
+ if (val == null) {
+ return nullTagValue();
+ }
+ return new LongArrayTagValue(val);
+ }
+
+ /**
+ * Construct a timestamp tag
+ *
+ * @param epochMilli payload in milliseconds
+ * @return Anonymous tag with timestamp payload
+ */
+ public static Serializable<BanyandbModel.TagValue> timestampTagValue(long epochMilli) {
+ return new TimestampTagValue(epochMilli);
+ }
+
+ public static Serializable<BanyandbModel.TagValue> nullTagValue() {
+ return NullTagValue.INSTANCE;
+ }
+
+ /**
+ * The value of a String type field.
+ */
+ public static class StringFieldValue extends Value<String> implements Serializable<BanyandbModel.FieldValue> {
+ private StringFieldValue(String value) {
+ super(value);
+ }
+
+ @Override
+ public BanyandbModel.FieldValue serialize() {
+ return BanyandbModel.FieldValue.newBuilder().setStr(BanyandbModel.Str.newBuilder().setValue(value)).build();
+ }
+ }
+
+ public static Serializable<BanyandbModel.FieldValue> stringFieldValue(String val) {
+ if (Strings.isNullOrEmpty(val)) {
+ return nullFieldValue();
+ }
+ return new StringFieldValue(val);
+ }
+
+ /**
+ * NullFieldValue is a value which can be converted to {@link com.google.protobuf.NullValue}.
+ * Users should use the singleton instead of create a new instance everytime.
+ */
+ public static class NullFieldValue extends Value<Object> implements Serializable<BanyandbModel.FieldValue> {
+ private static final NullFieldValue INSTANCE = new NullFieldValue();
+
+ private NullFieldValue() {
+ super(null);
+ }
+
+ @Override
+ public BanyandbModel.FieldValue serialize() {
+ return BanyandbModel.FieldValue.newBuilder().setNull(NULL_VALUE).build();
+ }
+ }
+
+ public static Serializable<BanyandbModel.FieldValue> nullFieldValue() {
+ return NullFieldValue.INSTANCE;
+ }
+
+ /**
+ * The value of an int64(Long) type field.
+ */
+ public static class LongFieldValue extends Value<Long> implements Serializable<BanyandbModel.FieldValue> {
+ private LongFieldValue(Long value) {
+ super(value);
+ }
+
+ @Override
+ public BanyandbModel.FieldValue serialize() {
+ return BanyandbModel.FieldValue.newBuilder().setInt(BanyandbModel.Int.newBuilder().setValue(value)).build();
+ }
+ }
+
+ /**
+ * The value of a float type field.
+ */
+ public static class FloatFieldValue extends Value<Float> implements Serializable<BanyandbModel.FieldValue> {
+ private FloatFieldValue(Float value) {
+ super(value);
+ }
+
+ @Override
+ public BanyandbModel.FieldValue serialize() {
+ return BanyandbModel.FieldValue.newBuilder().setFloat(BanyandbModel.Float.newBuilder().setValue(value)).build();
+ }
+ }
+
+ /**
+ * Construct a numeric tag
+ *
+ * @param val payload
+ * @return Anonymous tag with numeric payload
+ */
+ public static Serializable<BanyandbModel.FieldValue> longFieldValue(long val) {
+ return new LongFieldValue(val);
+ }
+
+ /**
+ * The value of a byte array(ByteString) type field.
+ */
+ public static class BinaryFieldValue extends Value<ByteString> implements Serializable<BanyandbModel.FieldValue> {
+ public BinaryFieldValue(ByteString byteString) {
+ super(byteString);
+ }
+
+ @Override
+ public BanyandbModel.FieldValue serialize() {
+ return BanyandbModel.FieldValue.newBuilder().setBinaryData(value).build();
+ }
+ }
+
+ /**
+ * Construct a byte array tag.
+ *
+ * @param bytes binary data
+ * @return Anonymous tag with binary payload
+ */
+ public static Serializable<BanyandbModel.FieldValue> binaryFieldValue(byte[] bytes) {
+ return new BinaryFieldValue(ByteString.copyFrom(bytes));
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/auth/AuthInterceptor.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/auth/AuthInterceptor.java
new file mode 100644
index 0000000000..ba3e5cb401
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/auth/AuthInterceptor.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.auth;
+
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.ClientInterceptor;
+import io.grpc.ForwardingClientCall;
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+
+public class AuthInterceptor implements ClientInterceptor {
+ private final String username;
+ private final String password;
+
+ private static final Metadata.Key<String> USERNAME_KEY =
+ Metadata.Key.of("username", Metadata.ASCII_STRING_MARSHALLER);
+ private static final Metadata.Key<String> PASSWORD_KEY =
+ Metadata.Key.of("password", Metadata.ASCII_STRING_MARSHALLER);
+
+ public AuthInterceptor(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ @Override
+ public <REQ_T, RESP_T> ClientCall<REQ_T, RESP_T> interceptCall(
+ MethodDescriptor<REQ_T, RESP_T> method,
+ CallOptions callOptions,
+ Channel next) {
+
+ return new ForwardingClientCall.SimpleForwardingClientCall<REQ_T, RESP_T>(
+ next.newCall(method, callOptions)) {
+ @Override
+ public void start(Listener<RESP_T> responseListener, Metadata headers) {
+ headers.put(USERNAME_KEY, username);
+ headers.put(PASSWORD_KEY, password);
+
+ super.start(responseListener, headers);
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/HandleExceptionsWith.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/HandleExceptionsWith.java
new file mode 100644
index 0000000000..5cddfcf51a
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/HandleExceptionsWith.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.grpc;
+
+import com.google.common.collect.Sets;
+import io.grpc.Status;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBGrpcApiExceptionFactory;
+
+public class HandleExceptionsWith {
+ private HandleExceptionsWith() {
+ }
+
+ private static final BanyanDBGrpcApiExceptionFactory EXCEPTION_FACTORY = new BanyanDBGrpcApiExceptionFactory(
+ // Exceptions caused by network issues are retryable
+ Sets.newHashSet(Status.Code.UNAVAILABLE, Status.Code.DEADLINE_EXCEEDED)
+ );
+
+ /**
+ * call the underlying operation and get response from the future.
+ *
+ * @param respSupplier a supplier which returns response
+ * @param <RESP> a generic type of user-defined gRPC response
+ * @return response in the type of defined in the gRPC protocol
+ * @throws BanyanDBException if the execution of the future itself thrown an exception
+ */
+ public static <RESP, E extends BanyanDBException> RESP callAndTranslateApiException(SupplierWithIO<RESP, E> respSupplier) throws BanyanDBException {
+ try {
+ return respSupplier.get();
+ } catch (Exception exception) {
+ throw EXCEPTION_FACTORY.createException(exception);
+ }
+ }
+
+ @FunctionalInterface
+ public interface SupplierWithIO<T, E extends Throwable> {
+ T get() throws E;
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/MetadataClient.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/MetadataClient.java
new file mode 100644
index 0000000000..f7e2529ba6
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/MetadataClient.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.grpc;
+
+import com.google.protobuf.GeneratedMessageV3;
+import io.grpc.stub.AbstractBlockingStub;
+import java.util.List;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.ResourceExist;
+
+/**
+ * abstract metadata client which defines CRUD operations for a specific kind of schema.
+ *
+ * @param <P> ProtoBuf: schema defined in ProtoBuf format
+ */
+public abstract class MetadataClient<STUB extends AbstractBlockingStub<STUB>, P extends GeneratedMessageV3> {
+ public static final long DEFAULT_MOD_REVISION = 0;
+
+ protected final STUB stub;
+
+ protected MetadataClient(STUB stub) {
+ this.stub = stub;
+ }
+
+ /**
+ * Create a schema
+ *
+ * @param payload the schema to be created
+ * @return the mod revision of the schema
+ * @throws BanyanDBException a wrapped exception to the underlying gRPC calls
+ */
+ public abstract long create(P payload) throws BanyanDBException;
+
+ /**
+ * Update the schema
+ *
+ * @param payload the schema which will be updated with the given name and group
+ * @throws BanyanDBException a wrapped exception to the underlying gRPC calls
+ */
+ public abstract void update(P payload) throws BanyanDBException;
+
+ /**
+ * Delete a schema
+ *
+ * @param group the group of the schema to be removed
+ * @param name the name of the schema to be removed
+ * @return whether this schema is deleted
+ * @throws BanyanDBException a wrapped exception to the underlying gRPC calls
+ */
+ public abstract boolean delete(String group, String name) throws BanyanDBException;
+
+ /**
+ * Get a schema with name
+ *
+ * @param group the group of the schema to be found
+ * @param name the name of the schema to be found
+ * @return the schema, null if not found
+ * @throws BanyanDBException a wrapped exception to the underlying gRPC calls
+ */
+ public abstract P get(String group, String name) throws BanyanDBException;
+
+ /**
+ * Check whether a schema exists
+ *
+ * @param group the group of the schema to be found
+ * @param name the name of the schema to be found
+ * @return whether resource exists
+ * @throws BanyanDBException a wrapped exception to the underlying gRPC calls
+ */
+ public abstract ResourceExist exist(String group, String name) throws BanyanDBException;
+
+ /**
+ * List all schemas with the same group name
+ *
+ * @return a list of schemas found
+ * @throws BanyanDBException a wrapped exception to the underlying gRPC calls
+ */
+ public abstract List<P> list(String group) throws BanyanDBException;
+
+ protected <REQ, RESP, E extends BanyanDBException> RESP execute(HandleExceptionsWith.SupplierWithIO<RESP, E> supplier) throws BanyanDBException {
+ return HandleExceptionsWith.callAndTranslateApiException(supplier);
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelFactory.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelFactory.java
index dbef0e0068..2a1e5452f5 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelFactory.java
@@ -16,20 +16,11 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.channel;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.ManagedChannel;
+import java.io.IOException;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
-
- }
-}
\ No newline at end of file
+public interface ChannelFactory {
+ ManagedChannel create() throws IOException;
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelManager.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelManager.java
new file mode 100644
index 0000000000..89a8d75685
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelManager.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.grpc.channel;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableSet;
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.ConnectivityState;
+import io.grpc.ForwardingClientCall;
+import io.grpc.ForwardingClientCallListener;
+import io.grpc.ManagedChannel;
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+import io.grpc.Status;
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ChannelManager extends ManagedChannel {
+ private final LazyReferenceChannel lazyChannel = new LazyReferenceChannel();
+
+ private static final Set<Status.Code> SC_NETWORK = ImmutableSet.of(
+ Status.Code.UNAVAILABLE, Status.Code.PERMISSION_DENIED,
+ Status.Code.UNAUTHENTICATED, Status.Code.RESOURCE_EXHAUSTED, Status.Code.UNKNOWN
+ );
+
+ private final ChannelManagerSettings settings;
+ private final ChannelFactory channelFactory;
+ private final ScheduledExecutorService executor;
+ @VisibleForTesting
+ final AtomicReference<Entry> entryRef = new AtomicReference<>();
+
+ public static ChannelManager create(ChannelManagerSettings settings, ChannelFactory channelFactory)
+ throws IOException {
+ return new ChannelManager(settings, channelFactory, Executors.newSingleThreadScheduledExecutor());
+ }
+
+ ChannelManager(ChannelManagerSettings settings, ChannelFactory channelFactory, ScheduledExecutorService executor) throws IOException {
+ this.settings = settings;
+ this.channelFactory = channelFactory;
+ this.executor = executor;
+
+ entryRef.set(new Entry(channelFactory.create()));
+
+ this.executor.scheduleAtFixedRate(
+ this::refreshSafely,
+ settings.getRefreshInterval(),
+ settings.getRefreshInterval(),
+ TimeUnit.SECONDS
+ );
+ }
+
+ private void refreshSafely() {
+ try {
+ refresh();
+ } catch (Exception e) {
+ log.warn("Failed to refresh channels", e);
+ }
+ }
+
+ void refresh() throws IOException {
+ Entry entry = entryRef.get();
+ if (!entry.needReconnect) {
+ return;
+ }
+ if (entry.isConnected(entry.reconnectCount.incrementAndGet() > this.settings.getForceReconnectionThreshold())) {
+ // Reconnect to the same server is automatically done by GRPC
+ // clear the flags
+ entry.reset();
+ return;
+ }
+ Entry replacedEntry = entryRef.getAndSet(new Entry(this.channelFactory.create()));
+ replacedEntry.shutdown();
+ }
+
+ @Override
+ public ManagedChannel shutdown() {
+ entryRef.get().channel.shutdown();
+ if (executor != null) {
+ // shutdownNow will cancel scheduled tasks
+ executor.shutdownNow();
+ }
+ return this;
+ }
+
+ @Override
+ public boolean isShutdown() {
+ if (!this.entryRef.get().channel.isShutdown()) {
+ return false;
+ }
+ return executor == null || executor.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ if (!this.entryRef.get().channel.isTerminated()) {
+ return false;
+ }
+ return executor == null || executor.isTerminated();
+ }
+
+ @Override
+ public ManagedChannel shutdownNow() {
+ entryRef.get().channel.shutdownNow();
+ if (executor != null) {
+ executor.shutdownNow();
+ }
+ return this;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ long endTimeNanos = System.nanoTime() + unit.toNanos(timeout);
+ entryRef.get().channel.awaitTermination(endTimeNanos - System.nanoTime(), TimeUnit.NANOSECONDS);
+ if (executor != null) {
+ long awaitTimeNanos = endTimeNanos - System.nanoTime();
+ executor.awaitTermination(awaitTimeNanos, TimeUnit.NANOSECONDS);
+ }
+ return isTerminated();
+ }
+
+ @Override
+ public <REQ, RESP> ClientCall<REQ, RESP> newCall(MethodDescriptor<REQ, RESP> methodDescriptor, CallOptions callOptions) {
+ return lazyChannel.newCall(methodDescriptor, callOptions);
+ }
+
+ @Override
+ public String authority() {
+ return this.entryRef.get().channel.authority();
+ }
+
+ @RequiredArgsConstructor
+ static class Entry {
+ final ManagedChannel channel;
+
+ final AtomicInteger reconnectCount = new AtomicInteger(0);
+
+ volatile boolean needReconnect = false;
+
+ boolean isConnected(boolean requestConnection) {
+ return this.channel.getState(requestConnection) == ConnectivityState.READY;
+ }
+
+ void shutdown() {
+ this.channel.shutdown();
+ }
+
+ void reset() {
+ needReconnect = false;
+ reconnectCount.set(0);
+ }
+ }
+
+ private class LazyReferenceChannel extends Channel {
+ @Override
+ public <REQ, RESP> ClientCall<REQ, RESP> newCall(MethodDescriptor<REQ, RESP> methodDescriptor, CallOptions callOptions) {
+ Entry entry = entryRef.get();
+
+ return new NetworkExceptionAwareClientCall<>(entry.channel.newCall(methodDescriptor, callOptions), entry);
+ }
+
+ @Override
+ public String authority() {
+ return ChannelManager.this.authority();
+ }
+ }
+
+ static class NetworkExceptionAwareClientCall<REQ, RESP> extends ForwardingClientCall.SimpleForwardingClientCall<REQ, RESP> {
+ final Entry entry;
+
+ public NetworkExceptionAwareClientCall(ClientCall<REQ, RESP> delegate, Entry entry) {
+ super(delegate);
+ this.entry = entry;
+ }
+
+ @Override
+ public void start(Listener<RESP> responseListener, Metadata headers) {
+ super.start(
+ new ForwardingClientCallListener.SimpleForwardingClientCallListener<RESP>(responseListener) {
+ @Override
+ public void onClose(Status status, Metadata trailers) {
+ if (isNetworkError(status)) {
+ entry.needReconnect = true;
+ }
+ super.onClose(status, trailers);
+ }
+ },
+ headers);
+ }
+ }
+
+ static boolean isNetworkError(Status status) {
+ return SC_NETWORK.contains(status.getCode());
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelManagerSettings.java
similarity index 65%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelManagerSettings.java
index dbef0e0068..043c3c0a83 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/ChannelManagerSettings.java
@@ -16,20 +16,15 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.channel;
+import lombok.Builder;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
-@RequiredArgsConstructor
+@Builder
@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
+public class ChannelManagerSettings {
+ private final long refreshInterval;
+ private final long forceReconnectionThreshold;
+}
- @Override
- public void onInsertCompleted() {
-
- }
-}
\ No newline at end of file
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/DefaultChannelFactory.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/DefaultChannelFactory.java
new file mode 100644
index 0000000000..089868c9dd
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/channel/DefaultChannelFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.grpc.channel;
+
+import io.grpc.ManagedChannel;
+import io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.NegotiationType;
+import io.grpc.netty.NettyChannelBuilder;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.util.internal.PlatformDependent;
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.library.banyandb.v1.client.Options;
+
+@Slf4j
+@RequiredArgsConstructor
+public class DefaultChannelFactory implements ChannelFactory {
+ private final URI[] targets;
+ private final Options options;
+
+ @Override
+ public ManagedChannel create() throws IOException {
+ NettyChannelBuilder managedChannelBuilder = NettyChannelBuilder.forAddress(resolveAddress())
+ .maxInboundMessageSize(options.getMaxInboundMessageSize())
+ .usePlaintext();
+
+ File caFile = new File(options.getSslTrustCAPath());
+ boolean isCAFileExist = caFile.exists() && caFile.isFile();
+ if (options.isForceTLS() || isCAFileExist) {
+ SslContextBuilder builder = GrpcSslContexts.forClient();
+
+ if (isCAFileExist) {
+ builder.trustManager(caFile);
+ }
+ managedChannelBuilder.negotiationType(NegotiationType.TLS).sslContext(builder.build());
+ }
+ return managedChannelBuilder.build();
+ }
+
+ private SocketAddress resolveAddress() throws UnknownHostException {
+ int numAddresses = this.targets.length;
+ if (numAddresses < 1) {
+ throw new UnknownHostException();
+ }
+ int offset = numAddresses == 1 ? 0 : PlatformDependent.threadLocalRandom().nextInt(numAddresses);
+ return new InetSocketAddress(this.targets[offset].getHost(), this.targets[offset].getPort());
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/AbortedException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/AbortedException.java
index dbef0e0068..a7b64e162b 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/AbortedException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class AbortedException extends BanyanDBException {
+ public AbortedException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public AbortedException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/AlreadyExistsException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/AlreadyExistsException.java
index dbef0e0068..2d9c08ddf2 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/AlreadyExistsException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class AlreadyExistsException extends BanyanDBException {
+ public AlreadyExistsException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public AlreadyExistsException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBApiExceptionFactory.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBApiExceptionFactory.java
new file mode 100644
index 0000000000..9cea391b7d
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBApiExceptionFactory.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
+
+import io.grpc.Status;
+
+class BanyanDBApiExceptionFactory {
+ private BanyanDBApiExceptionFactory() {
+ }
+
+ public static BanyanDBException createException(Throwable cause, Status.Code statusCode, boolean retryable) {
+ switch (statusCode) {
+ case CANCELLED:
+ return new CancelledException(cause, statusCode, retryable);
+ case NOT_FOUND:
+ return new NotFoundException(cause, statusCode, retryable);
+ case INVALID_ARGUMENT:
+ return new InvalidArgumentException(cause, statusCode, retryable);
+ case DEADLINE_EXCEEDED:
+ return new DeadlineExceededException(cause, statusCode, retryable);
+ case ALREADY_EXISTS:
+ return new AlreadyExistsException(cause, statusCode, retryable);
+ case PERMISSION_DENIED:
+ return new PermissionDeniedException(cause, statusCode, retryable);
+ case RESOURCE_EXHAUSTED:
+ return new ResourceExhaustedException(cause, statusCode, retryable);
+ case FAILED_PRECONDITION:
+ return new FailedPreconditionException(cause, statusCode, retryable);
+ case ABORTED:
+ return new AbortedException(cause, statusCode, retryable);
+ case OUT_OF_RANGE:
+ return new OutOfRangeException(cause, statusCode, retryable);
+ case UNIMPLEMENTED:
+ return new UnimplementedException(cause, statusCode, retryable);
+ case INTERNAL:
+ return new InternalException(cause, statusCode, retryable);
+ case UNAVAILABLE:
+ return new UnavailableException(cause, statusCode, retryable);
+ case DATA_LOSS:
+ return new DataLossException(cause, statusCode, retryable);
+ case UNAUTHENTICATED:
+ return new UnauthenticatedException(cause, statusCode, retryable);
+ case UNKNOWN: // Fall through.
+ default:
+ return new UnknownException(cause, statusCode, retryable);
+ }
+ }
+}
\ No newline at end of file
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMeasureUpdateRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBException.java
similarity index 51%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMeasureUpdateRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBException.java
index 763537a034..2371cdc582 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/measure/BanyanDBMeasureUpdateRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBException.java
@@ -16,23 +16,29 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.measure;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
+import com.google.common.base.Preconditions;
+import io.grpc.Status;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.MeasureWrite;
-import org.apache.skywalking.oap.server.core.UnexpectedException;
-import org.apache.skywalking.oap.server.library.client.request.UpdateRequest;
-@RequiredArgsConstructor
+/**
+ * BanyanDBException represents an exception thrown during a gRPC call.
+ */
@Getter
-public class BanyanDBMeasureUpdateRequest implements UpdateRequest {
- private final MeasureWrite measureWrite;
+public class BanyanDBException extends Exception {
+ private final Status.Code status;
+ private final boolean retryable;
+
+ public BanyanDBException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause);
+ this.status = Preconditions.checkNotNull(status);
+ this.retryable = retryable;
+ }
- @Override
- public void onUpdateFailure() {
- // BanyanDB measure update is equivalent to insert.
- // If something goes wrong, then it is a code bug or server-side is not available
- throw new UnexpectedException("Should not report onUpdateFailure when measure update.");
+ public BanyanDBException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause);
+ this.status = Preconditions.checkNotNull(status);
+ this.retryable = retryable;
}
}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBGrpcApiExceptionFactory.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBGrpcApiExceptionFactory.java
new file mode 100644
index 0000000000..d903740869
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/BanyanDBGrpcApiExceptionFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
+
+import com.google.common.collect.ImmutableSet;
+import io.grpc.Status;
+import io.grpc.StatusException;
+import io.grpc.StatusRuntimeException;
+import java.util.Set;
+
+public class BanyanDBGrpcApiExceptionFactory {
+ private final ImmutableSet<Status.Code> retryableCodes;
+
+ public BanyanDBGrpcApiExceptionFactory(Set<Status.Code> retryCodes) {
+ this.retryableCodes = ImmutableSet.copyOf(retryCodes);
+ }
+
+ public BanyanDBException createException(Throwable throwable) {
+ if (throwable instanceof StatusException) {
+ StatusException e = (StatusException) throwable;
+ return create(throwable, e.getStatus().getCode());
+ } else if (throwable instanceof StatusRuntimeException) {
+ StatusRuntimeException e = (StatusRuntimeException) throwable;
+ return create(throwable, e.getStatus().getCode());
+ } else if (throwable instanceof BanyanDBException) {
+ return (BanyanDBException) throwable;
+ } else {
+ // Do not retry on unknown throwable, even when UNKNOWN is in retryableCodes
+ return BanyanDBApiExceptionFactory.createException(
+ throwable, Status.Code.UNKNOWN, false);
+ }
+ }
+
+ private BanyanDBException create(Throwable throwable, Status.Code statusCode) {
+ boolean retryable = retryableCodes.contains(statusCode);
+ return BanyanDBApiExceptionFactory.createException(throwable, statusCode, retryable);
+ }
+}
\ No newline at end of file
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/CancelledException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/CancelledException.java
index 15dbc52c53..82f6900388 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/CancelledException.java
@@ -16,20 +16,17 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.stream;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.StreamWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBStreamInsertRequest implements InsertRequest {
- private final StreamWrite streamWrite;
-
- @Override
- public void onInsertCompleted() {
+public class CancelledException extends BanyanDBException {
+ public CancelledException(Throwable cause, Status.Code statusCode, boolean retryable) {
+ super(cause, statusCode, retryable);
+ }
+ public CancelledException(
+ String message, Throwable cause, Status.Code statusCode, boolean retryable) {
+ super(message, cause, statusCode, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/DataLossException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/DataLossException.java
index dbef0e0068..5374683b74 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/DataLossException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class DataLossException extends BanyanDBException {
+ public DataLossException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public DataLossException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/DeadlineExceededException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/DeadlineExceededException.java
index dbef0e0068..ee9bc086a3 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/DeadlineExceededException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class DeadlineExceededException extends BanyanDBException {
+ public DeadlineExceededException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public DeadlineExceededException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/FailedPreconditionException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/FailedPreconditionException.java
index 15dbc52c53..a95da49ada 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/FailedPreconditionException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.stream;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.StreamWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBStreamInsertRequest implements InsertRequest {
- private final StreamWrite streamWrite;
-
- @Override
- public void onInsertCompleted() {
+public class FailedPreconditionException extends BanyanDBException {
+ public FailedPreconditionException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public FailedPreconditionException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InternalException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InternalException.java
index dbef0e0068..c1550c4f07 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InternalException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class InternalException extends BanyanDBException {
+ public InternalException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public InternalException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InvalidArgumentException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InvalidArgumentException.java
index dbef0e0068..f73f610d13 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InvalidArgumentException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class InvalidArgumentException extends BanyanDBException {
+ public InvalidArgumentException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public InvalidArgumentException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InvalidReferenceException.java
similarity index 53%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InvalidReferenceException.java
index dbef0e0068..668af315ae 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/InvalidReferenceException.java
@@ -16,20 +16,23 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
+public class InvalidReferenceException extends BanyanDBException {
+ private final String refName;
- @Override
- public void onInsertCompleted() {
+ private InvalidReferenceException(String refName, String message) {
+ super(message, null, Status.Code.INVALID_ARGUMENT, false);
+ this.refName = refName;
+ }
+
+ public static InvalidReferenceException fromInvalidTag(String tagName) {
+ return new InvalidReferenceException(tagName, "invalid ref to tag " + tagName);
+ }
+ public static InvalidReferenceException fromInvalidField(String fieldName) {
+ return new InvalidReferenceException(fieldName, "invalid ref to field " + fieldName);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/NotFoundException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/NotFoundException.java
index dbef0e0068..4efce66397 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/NotFoundException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class NotFoundException extends BanyanDBException {
+ public NotFoundException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public NotFoundException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/OutOfRangeException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/OutOfRangeException.java
index dbef0e0068..ea6890b1c9 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/OutOfRangeException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class OutOfRangeException extends BanyanDBException {
+ public OutOfRangeException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public OutOfRangeException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/PermissionDeniedException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/PermissionDeniedException.java
index dbef0e0068..4bbc6b14f4 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/PermissionDeniedException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class PermissionDeniedException extends BanyanDBException {
+ public PermissionDeniedException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public PermissionDeniedException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/ResourceExhaustedException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/ResourceExhaustedException.java
index 15dbc52c53..328b95d3c0 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/ResourceExhaustedException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.stream;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.StreamWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBStreamInsertRequest implements InsertRequest {
- private final StreamWrite streamWrite;
-
- @Override
- public void onInsertCompleted() {
+public class ResourceExhaustedException extends BanyanDBException {
+ public ResourceExhaustedException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public ResourceExhaustedException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnauthenticatedException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnauthenticatedException.java
index dbef0e0068..c18ae43f49 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnauthenticatedException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class UnauthenticatedException extends BanyanDBException {
+ public UnauthenticatedException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public UnauthenticatedException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnavailableException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnavailableException.java
index dbef0e0068..0f31630623 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnavailableException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class UnavailableException extends BanyanDBException {
+ public UnavailableException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public UnavailableException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnimplementedException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnimplementedException.java
index dbef0e0068..3003c7a62a 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnimplementedException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class UnimplementedException extends BanyanDBException {
+ public UnimplementedException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public UnimplementedException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnknownException.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnknownException.java
index dbef0e0068..c41ef58a6e 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/grpc/exception/UnknownException.java
@@ -16,20 +16,16 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.grpc.exception;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import io.grpc.Status;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+public class UnknownException extends BanyanDBException {
+ public UnknownException(Throwable cause, Status.Code status, boolean retryable) {
+ super(cause, status, retryable);
+ }
+ public UnknownException(String message, Throwable cause, Status.Code status, boolean retryable) {
+ super(message, cause, status, retryable);
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/Duration.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/Duration.java
new file mode 100644
index 0000000000..b6e570ca94
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/Duration.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import com.google.common.base.Strings;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode
+public class Duration {
+ private static final Pattern DURATION_PATTERN =
+ Pattern.compile("(((?<day>\\d+)d)?((?<hour>\\d+)h)?((?<minute>\\d+)m)?|0)");
+ private static final long MINUTES_PER_HOUR = 60;
+ private static final long HOURS_PER_DAY = 24;
+ private static final long MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY;
+
+ @EqualsAndHashCode.Exclude
+ private volatile String text;
+ private final long minutes;
+
+ private Duration(long minutes) {
+ this.minutes = minutes;
+ }
+
+ public String format() {
+ if (!Strings.isNullOrEmpty(text)) {
+ return text;
+ }
+
+ final StringBuilder builder = new StringBuilder();
+ long minutes = this.minutes;
+ if (minutes >= MINUTES_PER_DAY) {
+ long weeks = minutes / MINUTES_PER_DAY;
+ builder.append(weeks).append("d");
+ minutes = minutes % MINUTES_PER_DAY;
+ }
+ if (minutes >= MINUTES_PER_HOUR) {
+ long weeks = minutes / MINUTES_PER_HOUR;
+ builder.append(weeks).append("h");
+ minutes = minutes % MINUTES_PER_HOUR;
+ }
+ if (minutes > 0) {
+ builder.append(minutes).append("m");
+ }
+ this.text = builder.toString();
+ return this.text;
+ }
+
+ public Duration add(Duration duration) {
+ return new Duration(this.minutes + duration.minutes);
+ }
+
+ public static Duration parse(String text) {
+ if (Strings.isNullOrEmpty(text)) {
+ return new Duration(0);
+ }
+ Matcher matcher = DURATION_PATTERN.matcher(text);
+ if (!matcher.find()) {
+ return new Duration(0);
+ }
+ long total = 0;
+ final String days = matcher.group("day");
+ if (!Strings.isNullOrEmpty(days)) {
+ total += Long.parseLong(days) * MINUTES_PER_DAY;
+ }
+ final String hours = matcher.group("hour");
+ if (!Strings.isNullOrEmpty(hours)) {
+ total += Long.parseLong(hours) * MINUTES_PER_HOUR;
+ }
+ final String minutes = matcher.group("minute");
+ if (!Strings.isNullOrEmpty(minutes)) {
+ total += Long.parseLong(minutes);
+ }
+ return new Duration(total);
+ }
+
+ public static Duration ofMinutes(long minutes) {
+ return new Duration(minutes);
+ }
+
+ public static Duration ofHours(long hours) {
+ return new Duration(hours * MINUTES_PER_HOUR);
+ }
+
+ public static Duration ofDays(long days) {
+ return ofHours(days * HOURS_PER_DAY);
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/GroupMetadataRegistry.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/GroupMetadataRegistry.java
new file mode 100644
index 0000000000..66243d020b
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/GroupMetadataRegistry.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Group;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.GroupRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.MetadataClient;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class GroupMetadataRegistry extends MetadataClient<GroupRegistryServiceGrpc.GroupRegistryServiceBlockingStub,
+ Group> {
+
+ public GroupMetadataRegistry(Channel channel) {
+ super(GroupRegistryServiceGrpc.newBlockingStub(channel));
+ }
+
+ @Override
+ public long create(Group payload) throws BanyanDBException {
+ execute(() -> stub.create(BanyandbDatabase.GroupRegistryServiceCreateRequest.newBuilder()
+ .setGroup(payload)
+ .build()));
+ return DEFAULT_MOD_REVISION;
+ }
+
+ @Override
+ public void update(Group payload) throws BanyanDBException {
+ execute(() -> stub.update(BanyandbDatabase.GroupRegistryServiceUpdateRequest.newBuilder()
+ .setGroup(payload)
+ .build()));
+ }
+
+ @Override
+ public boolean delete(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.GroupRegistryServiceDeleteResponse resp = execute(() ->
+ stub.delete(BanyandbDatabase.GroupRegistryServiceDeleteRequest.newBuilder()
+ .setGroup(name)
+ .build()));
+ return resp != null && resp.getDeleted();
+ }
+
+ @Override
+ public Group get(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.GroupRegistryServiceGetResponse resp = execute(() ->
+ stub.get(BanyandbDatabase.GroupRegistryServiceGetRequest.newBuilder()
+ .setGroup(name)
+ .build()));
+
+ return resp.getGroup();
+ }
+
+ @Override
+ public ResourceExist exist(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.GroupRegistryServiceExistResponse resp = execute(() ->
+ stub.exist(BanyandbDatabase.GroupRegistryServiceExistRequest.newBuilder()
+ .setGroup(name)
+ .build()));
+ return ResourceExist.builder().hasGroup(resp.getHasGroup()).hasResource(resp.getHasGroup()).build();
+ }
+
+ @Override
+ public List<Group> list(String group) throws BanyanDBException {
+ BanyandbDatabase.GroupRegistryServiceListResponse resp = execute(() ->
+ stub.list(BanyandbDatabase.GroupRegistryServiceListRequest.newBuilder()
+ .build()));
+
+ return resp.getGroupList();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/IndexRuleBindingMetadataRegistry.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/IndexRuleBindingMetadataRegistry.java
new file mode 100644
index 0000000000..d5bbffa2ab
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/IndexRuleBindingMetadataRegistry.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.IndexRuleBinding;
+import org.apache.skywalking.banyandb.database.v1.IndexRuleBindingRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.MetadataClient;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class IndexRuleBindingMetadataRegistry extends MetadataClient<IndexRuleBindingRegistryServiceGrpc.IndexRuleBindingRegistryServiceBlockingStub,
+ IndexRuleBinding> {
+
+ public IndexRuleBindingMetadataRegistry(Channel channel) {
+ super(IndexRuleBindingRegistryServiceGrpc.newBlockingStub(channel));
+ }
+
+ @Override
+ public long create(IndexRuleBinding payload) throws BanyanDBException {
+ execute(() -> stub.create(BanyandbDatabase.IndexRuleBindingRegistryServiceCreateRequest.newBuilder()
+ .setIndexRuleBinding(payload)
+ .build()));
+ return DEFAULT_MOD_REVISION;
+ }
+
+ @Override
+ public void update(IndexRuleBinding payload) throws BanyanDBException {
+ execute(() -> stub.update(BanyandbDatabase.IndexRuleBindingRegistryServiceUpdateRequest.newBuilder()
+ .setIndexRuleBinding(payload)
+ .build()));
+ }
+
+ @Override
+ public boolean delete(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.IndexRuleBindingRegistryServiceDeleteResponse resp = execute(() ->
+ stub.delete(BanyandbDatabase.IndexRuleBindingRegistryServiceDeleteRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return resp != null && resp.getDeleted();
+ }
+
+ @Override
+ public IndexRuleBinding get(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.IndexRuleBindingRegistryServiceGetResponse resp = execute(() ->
+ stub.get(BanyandbDatabase.IndexRuleBindingRegistryServiceGetRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+
+ return resp.getIndexRuleBinding();
+ }
+
+ @Override
+ public ResourceExist exist(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.IndexRuleBindingRegistryServiceExistResponse resp = execute(() ->
+ stub.exist(BanyandbDatabase.IndexRuleBindingRegistryServiceExistRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return ResourceExist.builder().hasGroup(resp.getHasGroup()).hasResource(resp.getHasIndexRuleBinding()).build();
+ }
+
+ @Override
+ public List<IndexRuleBinding> list(String group) throws BanyanDBException {
+ BanyandbDatabase.IndexRuleBindingRegistryServiceListResponse resp = execute(() ->
+ stub.list(BanyandbDatabase.IndexRuleBindingRegistryServiceListRequest.newBuilder()
+ .setGroup(group)
+ .build()));
+
+ return resp.getIndexRuleBindingList();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/IndexRuleMetadataRegistry.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/IndexRuleMetadataRegistry.java
new file mode 100644
index 0000000000..27cdcf61e0
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/IndexRuleMetadataRegistry.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.IndexRule;
+import org.apache.skywalking.banyandb.database.v1.IndexRuleRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.MetadataClient;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class IndexRuleMetadataRegistry extends MetadataClient<IndexRuleRegistryServiceGrpc.IndexRuleRegistryServiceBlockingStub,
+ IndexRule> {
+ public IndexRuleMetadataRegistry(Channel channel) {
+ super(IndexRuleRegistryServiceGrpc.newBlockingStub(channel));
+ }
+
+ @Override
+ public long create(IndexRule payload) throws BanyanDBException {
+ execute(() ->
+ stub.create(BanyandbDatabase.IndexRuleRegistryServiceCreateRequest.newBuilder()
+ .setIndexRule(payload)
+ .build()));
+ return DEFAULT_MOD_REVISION;
+ }
+
+ @Override
+ public void update(IndexRule payload) throws BanyanDBException {
+ execute(() ->
+ stub.update(BanyandbDatabase.IndexRuleRegistryServiceUpdateRequest.newBuilder()
+ .setIndexRule(payload)
+ .build()));
+ }
+
+ @Override
+ public boolean delete(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.IndexRuleRegistryServiceDeleteResponse resp = execute(() ->
+ stub.delete(BanyandbDatabase.IndexRuleRegistryServiceDeleteRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return resp != null && resp.getDeleted();
+ }
+
+ @Override
+ public IndexRule get(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.IndexRuleRegistryServiceGetResponse resp = execute(() ->
+ stub.get(BanyandbDatabase.IndexRuleRegistryServiceGetRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+
+ return resp.getIndexRule();
+ }
+
+ @Override
+ public ResourceExist exist(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.IndexRuleRegistryServiceExistResponse resp = execute(() ->
+ stub.exist(BanyandbDatabase.IndexRuleRegistryServiceExistRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return ResourceExist.builder().hasGroup(resp.getHasGroup()).hasResource(resp.getHasIndexRule()).build();
+ }
+
+ @Override
+ public List<IndexRule> list(String group) throws BanyanDBException {
+ BanyandbDatabase.IndexRuleRegistryServiceListResponse resp = execute(() ->
+ stub.list(BanyandbDatabase.IndexRuleRegistryServiceListRequest.newBuilder()
+ .setGroup(group)
+ .build()));
+
+ return resp.getIndexRuleList();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/MeasureMetadataRegistry.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/MeasureMetadataRegistry.java
new file mode 100644
index 0000000000..1e3be1b421
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/MeasureMetadataRegistry.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Measure;
+import org.apache.skywalking.banyandb.database.v1.MeasureRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.MetadataClient;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class MeasureMetadataRegistry extends MetadataClient<MeasureRegistryServiceGrpc.MeasureRegistryServiceBlockingStub,
+ Measure> {
+
+ public MeasureMetadataRegistry(Channel channel) {
+ super(MeasureRegistryServiceGrpc.newBlockingStub(channel));
+ }
+
+ @Override
+ public long create(final Measure payload) throws BanyanDBException {
+ BanyandbDatabase.MeasureRegistryServiceCreateResponse resp = execute(() ->
+ stub.create(BanyandbDatabase.MeasureRegistryServiceCreateRequest.newBuilder()
+ .setMeasure(payload)
+ .build()));
+ return resp.getModRevision();
+ }
+
+ @Override
+ public void update(final Measure payload) throws BanyanDBException {
+ execute(() ->
+ stub.update(BanyandbDatabase.MeasureRegistryServiceUpdateRequest.newBuilder()
+ .setMeasure(payload)
+ .build()));
+ }
+
+ @Override
+ public boolean delete(final String group, final String name) throws BanyanDBException {
+ BanyandbDatabase.MeasureRegistryServiceDeleteResponse resp = execute(() ->
+ stub.delete(BanyandbDatabase.MeasureRegistryServiceDeleteRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return resp != null && resp.getDeleted();
+ }
+
+ @Override
+ public Measure get(final String group, final String name) throws BanyanDBException {
+ BanyandbDatabase.MeasureRegistryServiceGetResponse resp = execute(() ->
+ stub.get(BanyandbDatabase.MeasureRegistryServiceGetRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+
+ return resp.getMeasure();
+ }
+
+ @Override
+ public ResourceExist exist(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.MeasureRegistryServiceExistResponse resp = execute(() ->
+ stub.exist(BanyandbDatabase.MeasureRegistryServiceExistRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return ResourceExist.builder().hasGroup(resp.getHasGroup()).hasResource(resp.getHasMeasure()).build();
+ }
+
+ @Override
+ public List<Measure> list(final String group) throws BanyanDBException {
+ BanyandbDatabase.MeasureRegistryServiceListResponse resp = execute(() ->
+ stub.list(BanyandbDatabase.MeasureRegistryServiceListRequest.newBuilder()
+ .setGroup(group)
+ .build()));
+
+ return resp.getMeasureList();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/PropertyMetadataRegistry.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/PropertyMetadataRegistry.java
new file mode 100644
index 0000000000..ae543bc6aa
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/PropertyMetadataRegistry.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property;
+import org.apache.skywalking.banyandb.database.v1.PropertyRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.MetadataClient;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class PropertyMetadataRegistry extends MetadataClient<PropertyRegistryServiceGrpc.PropertyRegistryServiceBlockingStub,
+ Property> {
+
+ public PropertyMetadataRegistry(Channel channel) {
+ super(PropertyRegistryServiceGrpc.newBlockingStub(channel));
+ }
+
+ @Override
+ public long create(final Property payload) throws BanyanDBException {
+ BanyandbDatabase.PropertyRegistryServiceCreateResponse resp = execute(() ->
+ stub.create(BanyandbDatabase.PropertyRegistryServiceCreateRequest.newBuilder()
+ .setProperty(payload)
+ .build()));
+ return resp.getModRevision();
+ }
+
+ @Override
+ public void update(final Property payload) throws BanyanDBException {
+ execute(() ->
+ stub.update(BanyandbDatabase.PropertyRegistryServiceUpdateRequest.newBuilder()
+ .setProperty(payload)
+ .build()));
+ }
+
+ @Override
+ public boolean delete(final String group, final String name) throws BanyanDBException {
+ BanyandbDatabase.PropertyRegistryServiceDeleteResponse resp = execute(() ->
+ stub.delete(BanyandbDatabase.PropertyRegistryServiceDeleteRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return resp != null && resp.getDeleted();
+ }
+
+ @Override
+ public Property get(final String group, final String name) throws BanyanDBException {
+ BanyandbDatabase.PropertyRegistryServiceGetResponse resp = execute(() ->
+ stub.get(BanyandbDatabase.PropertyRegistryServiceGetRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+
+ return resp.getProperty();
+ }
+
+ @Override
+ public ResourceExist exist(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.PropertyRegistryServiceExistResponse resp = execute(() ->
+ stub.exist(BanyandbDatabase.PropertyRegistryServiceExistRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return ResourceExist.builder().hasGroup(resp.getHasGroup()).hasResource(resp.getHasProperty()).build();
+ }
+
+ @Override
+ public List<Property> list(final String group) throws BanyanDBException {
+ BanyandbDatabase.PropertyRegistryServiceListResponse resp = execute(() ->
+ stub.list(BanyandbDatabase.PropertyRegistryServiceListRequest.newBuilder()
+ .setGroup(group)
+ .build()));
+
+ return resp.getPropertiesList();
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/ResourceExist.java
similarity index 65%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/ResourceExist.java
index dbef0e0068..76d698b48d 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/ResourceExist.java
@@ -16,20 +16,14 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.metadata;
+import lombok.Builder;
import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
-@RequiredArgsConstructor
+@Builder
@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
-
- }
-}
\ No newline at end of file
+public class ResourceExist {
+ private final boolean hasGroup;
+ private final boolean hasResource;
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/Serializable.java
similarity index 62%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/Serializable.java
index 15dbc52c53..d3a4856fbb 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/stream/BanyanDBStreamInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/Serializable.java
@@ -16,20 +16,19 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.stream;
+package org.apache.skywalking.library.banyandb.v1.client.metadata;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.StreamWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
-
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBStreamInsertRequest implements InsertRequest {
- private final StreamWrite streamWrite;
-
- @Override
- public void onInsertCompleted() {
-
- }
-}
\ No newline at end of file
+/**
+ * Serializable provide serialize method to build a protobuf message.
+ * This is normally for inner types of metadata.
+ *
+ * @param <P> the produced class must be in Protobuf message type.
+ */
+public interface Serializable<P extends com.google.protobuf.GeneratedMessageV3> {
+ /**
+ * Serialize the object to the protobuf format
+ *
+ * @return protobuf representation
+ */
+ P serialize();
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/StreamMetadataRegistry.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/StreamMetadataRegistry.java
new file mode 100644
index 0000000000..efcb663707
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/StreamMetadataRegistry.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Stream;
+import org.apache.skywalking.banyandb.database.v1.StreamRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.MetadataClient;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class StreamMetadataRegistry extends MetadataClient<StreamRegistryServiceGrpc.StreamRegistryServiceBlockingStub,
+ Stream> {
+
+ public StreamMetadataRegistry(Channel channel) {
+ super(StreamRegistryServiceGrpc.newBlockingStub(channel));
+ }
+
+ @Override
+ public long create(Stream payload) throws BanyanDBException {
+ BanyandbDatabase.StreamRegistryServiceCreateResponse resp = execute(() ->
+ stub.create(BanyandbDatabase.StreamRegistryServiceCreateRequest.newBuilder()
+ .setStream(payload)
+ .build()));
+ return resp.getModRevision();
+ }
+
+ @Override
+ public void update(Stream payload) throws BanyanDBException {
+ execute(() ->
+ stub.update(BanyandbDatabase.StreamRegistryServiceUpdateRequest.newBuilder()
+ .setStream(payload)
+ .build()));
+ }
+
+ @Override
+ public boolean delete(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.StreamRegistryServiceDeleteResponse resp = execute(() ->
+ stub.delete(BanyandbDatabase.StreamRegistryServiceDeleteRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return resp != null && resp.getDeleted();
+ }
+
+ @Override
+ public Stream get(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.StreamRegistryServiceGetResponse resp = execute(() ->
+ stub.get(BanyandbDatabase.StreamRegistryServiceGetRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+
+ return resp.getStream();
+ }
+
+ @Override
+ public ResourceExist exist(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.StreamRegistryServiceExistResponse resp = execute(() ->
+ stub.exist(BanyandbDatabase.StreamRegistryServiceExistRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return ResourceExist.builder().hasGroup(resp.getHasGroup()).hasResource(resp.getHasStream()).build();
+ }
+
+ @Override
+ public List<Stream> list(String group) throws BanyanDBException {
+ BanyandbDatabase.StreamRegistryServiceListResponse resp = execute(() ->
+ stub.list(BanyandbDatabase.StreamRegistryServiceListRequest.newBuilder()
+ .setGroup(group)
+ .build()));
+
+ return resp.getStreamList();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/TopNAggregationMetadataRegistry.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/TopNAggregationMetadataRegistry.java
new file mode 100644
index 0000000000..f5813e1cac
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/TopNAggregationMetadataRegistry.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TopNAggregation;
+import org.apache.skywalking.banyandb.database.v1.TopNAggregationRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.MetadataClient;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class TopNAggregationMetadataRegistry extends MetadataClient<TopNAggregationRegistryServiceGrpc.TopNAggregationRegistryServiceBlockingStub,
+ TopNAggregation> {
+ public TopNAggregationMetadataRegistry(Channel channel) {
+ super(TopNAggregationRegistryServiceGrpc.newBlockingStub(channel));
+ }
+
+ @Override
+ public long create(TopNAggregation payload) throws BanyanDBException {
+ execute(() ->
+ stub.create(BanyandbDatabase.TopNAggregationRegistryServiceCreateRequest.newBuilder()
+ .setTopNAggregation(payload)
+ .build()));
+ return DEFAULT_MOD_REVISION;
+ }
+
+ @Override
+ public void update(TopNAggregation payload) throws BanyanDBException {
+ execute(() ->
+ stub.update(BanyandbDatabase.TopNAggregationRegistryServiceUpdateRequest.newBuilder()
+ .setTopNAggregation(payload)
+ .build()));
+ }
+
+ @Override
+ public boolean delete(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.TopNAggregationRegistryServiceDeleteResponse resp = execute(() ->
+ stub.delete(BanyandbDatabase.TopNAggregationRegistryServiceDeleteRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return resp != null && resp.getDeleted();
+ }
+
+ @Override
+ public TopNAggregation get(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.TopNAggregationRegistryServiceGetResponse resp = execute(() ->
+ stub.get(BanyandbDatabase.TopNAggregationRegistryServiceGetRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+
+ return resp.getTopNAggregation();
+ }
+
+ @Override
+ public ResourceExist exist(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.TopNAggregationRegistryServiceExistResponse resp = execute(() ->
+ stub.exist(BanyandbDatabase.TopNAggregationRegistryServiceExistRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return ResourceExist.builder().hasGroup(resp.getHasGroup()).hasResource(resp.getHasTopNAggregation()).build();
+ }
+
+ @Override
+ public List<TopNAggregation> list(String group) throws BanyanDBException {
+ BanyandbDatabase.TopNAggregationRegistryServiceListResponse resp = execute(() ->
+ stub.list(BanyandbDatabase.TopNAggregationRegistryServiceListRequest.newBuilder()
+ .setGroup(group)
+ .build()));
+
+ return resp.getTopNAggregationList();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/TraceMetadataRegistry.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/TraceMetadataRegistry.java
new file mode 100644
index 0000000000..64fc61afe0
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/metadata/TraceMetadataRegistry.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.metadata;
+
+import io.grpc.Channel;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Trace;
+import org.apache.skywalking.banyandb.database.v1.TraceRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.MetadataClient;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+
+public class TraceMetadataRegistry extends MetadataClient<TraceRegistryServiceGrpc.TraceRegistryServiceBlockingStub,
+ Trace> {
+
+ public TraceMetadataRegistry(Channel channel) {
+ super(TraceRegistryServiceGrpc.newBlockingStub(channel));
+ }
+
+ @Override
+ public long create(Trace payload) throws BanyanDBException {
+ BanyandbDatabase.TraceRegistryServiceCreateResponse resp = execute(() ->
+ stub.create(BanyandbDatabase.TraceRegistryServiceCreateRequest.newBuilder()
+ .setTrace(payload)
+ .build()));
+ return resp.getModRevision();
+ }
+
+ @Override
+ public void update(Trace payload) throws BanyanDBException {
+ execute(() ->
+ stub.update(BanyandbDatabase.TraceRegistryServiceUpdateRequest.newBuilder()
+ .setTrace(payload)
+ .build()));
+ }
+
+ @Override
+ public boolean delete(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.TraceRegistryServiceDeleteResponse resp = execute(() ->
+ stub.delete(BanyandbDatabase.TraceRegistryServiceDeleteRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return resp != null && resp.getDeleted();
+ }
+
+ @Override
+ public Trace get(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.TraceRegistryServiceGetResponse resp = execute(() ->
+ stub.get(BanyandbDatabase.TraceRegistryServiceGetRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+
+ return resp.getTrace();
+ }
+
+ @Override
+ public ResourceExist exist(String group, String name) throws BanyanDBException {
+ BanyandbDatabase.TraceRegistryServiceExistResponse resp = execute(() ->
+ stub.exist(BanyandbDatabase.TraceRegistryServiceExistRequest.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
+ .build()));
+ return ResourceExist.builder().hasGroup(resp.getHasGroup()).hasResource(resp.getHasTrace()).build();
+ }
+
+ @Override
+ public List<Trace> list(String group) throws BanyanDBException {
+ BanyandbDatabase.TraceRegistryServiceListResponse resp = execute(() ->
+ stub.list(BanyandbDatabase.TraceRegistryServiceListRequest.newBuilder()
+ .setGroup(group)
+ .build()));
+
+ return resp.getTraceList();
+ }
+}
\ No newline at end of file
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/CopyOnWriteMap.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/CopyOnWriteMap.java
new file mode 100644
index 0000000000..9a9f745110
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/CopyOnWriteMap.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ *
+ */
+
+/*
+ * Copyright 2015 Confluent Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Original license:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
+ * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
+ * to You 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 org.apache.skywalking.library.banyandb.v1.client.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * A simple read-optimized map implementation that synchronizes only writes and does a full copy on
+ * each modification
+ */
+public class CopyOnWriteMap<K, V> implements ConcurrentMap<K, V> {
+
+ private volatile Map<K, V> map;
+
+ public CopyOnWriteMap() {
+ this.map = Collections.emptyMap();
+ }
+
+ public CopyOnWriteMap(Map<K, V> map) {
+ this.map = Collections.unmodifiableMap(map);
+ }
+
+ @Override
+ public boolean containsKey(Object k) {
+ return map.containsKey(k);
+ }
+
+ @Override
+ public boolean containsValue(Object v) {
+ return map.containsValue(v);
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return map.entrySet();
+ }
+
+ @Override
+ public V get(Object k) {
+ return map.get(k);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return map.keySet();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return map.values();
+ }
+
+ @Override
+ public synchronized void clear() {
+ this.map = Collections.emptyMap();
+ }
+
+ @Override
+ public synchronized V put(K k, V v) {
+ Map<K, V> copy = new HashMap<K, V>(this.map);
+ V prev = copy.put(k, v);
+ this.map = Collections.unmodifiableMap(copy);
+ return prev;
+ }
+
+ @Override
+ public synchronized void putAll(Map<? extends K, ? extends V> entries) {
+ Map<K, V> copy = new HashMap<K, V>(this.map);
+ copy.putAll(entries);
+ this.map = Collections.unmodifiableMap(copy);
+ }
+
+ @Override
+ public synchronized V remove(Object key) {
+ Map<K, V> copy = new HashMap<K, V>(this.map);
+ V prev = copy.remove(key);
+ this.map = Collections.unmodifiableMap(copy);
+ return prev;
+ }
+
+ @Override
+ public synchronized V putIfAbsent(K k, V v) {
+ if (!containsKey(k)) {
+ return put(k, v);
+ } else {
+ return get(k);
+ }
+ }
+
+ @Override
+ public synchronized boolean remove(Object k, Object v) {
+ if (containsKey(k) && get(k).equals(v)) {
+ remove(k);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public synchronized boolean replace(K k, V original, V replacement) {
+ if (containsKey(k) && get(k).equals(original)) {
+ put(k, replacement);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public synchronized V replace(K k, V v) {
+ if (containsKey(k)) {
+ return put(k, v);
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/IgnoreHashEquals.java
similarity index 63%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/IgnoreHashEquals.java
index dbef0e0068..d33e0a0c3e 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/IgnoreHashEquals.java
@@ -16,20 +16,17 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.util;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
- @Override
- public void onInsertCompleted() {
-
- }
-}
\ No newline at end of file
+@Retention(SOURCE)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface IgnoreHashEquals {
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/PrivateKeyUtil.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/PrivateKeyUtil.java
new file mode 100644
index 0000000000..76ff08d338
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/PrivateKeyUtil.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Base64;
+
+/**
+ * Util intends to parse PKCS#1 and PKCS#8 at same time.
+ */
+public class PrivateKeyUtil {
+ private static final String PKCS_1_PEM_HEADER = "-----BEGIN RSA PRIVATE KEY-----";
+ private static final String PKCS_1_PEM_FOOTER = "-----END RSA PRIVATE KEY-----";
+ private static final String PKCS_8_PEM_HEADER = "-----BEGIN PRIVATE KEY-----";
+ private static final String PKCS_8_PEM_FOOTER = "-----END PRIVATE KEY-----";
+
+ /**
+ * Load a RSA decryption key from a file (PEM or DER).
+ */
+ public static InputStream loadDecryptionKey(String keyFilePath) throws IOException {
+ byte[] keyDataBytes = Files.readAllBytes(Paths.get(keyFilePath));
+ String keyDataString = new String(keyDataBytes, StandardCharsets.UTF_8);
+
+ if (keyDataString.contains(PKCS_1_PEM_HEADER)) {
+ // OpenSSL / PKCS#1 Base64 PEM encoded file
+ keyDataString = keyDataString.replace(PKCS_1_PEM_HEADER, "");
+ keyDataString = keyDataString.replace(PKCS_1_PEM_FOOTER, "");
+ keyDataString = keyDataString.replace("\n", "");
+ return readPkcs1PrivateKey(Base64.getDecoder().decode(keyDataString));
+ }
+
+ return new ByteArrayInputStream(keyDataString.getBytes());
+ }
+
+ /**
+ * Create a InputStream instance from raw PKCS#1 bytes. Raw Java API can't recognize ASN.1 format, so we should
+ * convert it into a pkcs#8 format Java can understand.
+ */
+ private static InputStream readPkcs1PrivateKey(byte[] pkcs1Bytes) {
+ int pkcs1Length = pkcs1Bytes.length;
+ int totalLength = pkcs1Length + 22;
+ byte[] pkcs8Header = new byte[]{
+ 0x30, (byte) 0x82, (byte) ((totalLength >> 8) & 0xff), (byte) (totalLength & 0xff), // Sequence + total length
+ 0x2, 0x1, 0x0, // Integer (0)
+ 0x30, 0xD, 0x6, 0x9, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0xD, 0x1, 0x1, 0x1, 0x5, 0x0, // Sequence: 1.2.840.113549.1.1.1, NULL
+ 0x4, (byte) 0x82, (byte) ((pkcs1Length >> 8) & 0xff), (byte) (pkcs1Length & 0xff) // Octet string + length
+ };
+ StringBuilder pkcs8 = new StringBuilder(PKCS_8_PEM_HEADER);
+ pkcs8.append("\n").append(new String(Base64.getEncoder().encode(join(pkcs8Header, pkcs1Bytes))));
+ pkcs8.append("\n").append(PKCS_8_PEM_FOOTER);
+ return new ByteArrayInputStream(pkcs8.toString().getBytes());
+ }
+
+ private static byte[] join(byte[] byteArray1, byte[] byteArray2) {
+ byte[] bytes = new byte[byteArray1.length + byteArray2.length];
+ System.arraycopy(byteArray1, 0, bytes, 0, byteArray1.length);
+ System.arraycopy(byteArray2, 0, bytes, byteArray1.length, byteArray2.length);
+ return bytes;
+ }
+}
diff --git a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/StatusUtil.java
similarity index 52%
copy from oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
copy to oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/StatusUtil.java
index dbef0e0068..b15ec39cc6 100644
--- a/oap-server/server-storage-plugin/storage-banyandb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/banyandb/trace/BanyanDBTraceInsertRequest.java
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/StatusUtil.java
@@ -16,20 +16,27 @@
*
*/
-package org.apache.skywalking.oap.server.storage.plugin.banyandb.trace;
+package org.apache.skywalking.library.banyandb.v1.client.util;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.banyandb.v1.client.TraceWrite;
-import org.apache.skywalking.oap.server.library.client.request.InsertRequest;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
-@RequiredArgsConstructor
-@Getter
-public class BanyanDBTraceInsertRequest implements InsertRequest {
- private final TraceWrite traceWrite;
-
- @Override
- public void onInsertCompleted() {
+/**
+ * Status is a utility class for converting between strings and Status enums.
+ */
+public class StatusUtil {
+ /**
+ * Convert a Status enum to a string.
+ *
+ * @param statusString the string to convert
+ * @return the Status enum
+ */
+ public static BanyandbModel.Status convertStringToStatus(String statusString) {
+ try {
+ return BanyandbModel.Status.valueOf(statusString);
+ } catch (IllegalArgumentException e) {
+ // Return a specific enum value for unknown strings
+ return BanyandbModel.Status.STATUS_UNSPECIFIED;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/TimeUtils.java b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/TimeUtils.java
new file mode 100644
index 0000000000..d65a5318d2
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/java/org/apache/skywalking/library/banyandb/v1/client/util/TimeUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client.util;
+
+import com.google.protobuf.Timestamp;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+public class TimeUtils {
+ public static ZonedDateTime parseTimestamp(Timestamp ts) {
+ return Instant.ofEpochSecond(ts.getSeconds(), ts.getNanos()).atZone(ZoneId.systemDefault());
+ }
+
+ public static Timestamp buildTimestamp(ZonedDateTime zdt) {
+ if (zdt == null) {
+ return null;
+ }
+ return Timestamp.newBuilder()
+ .setSeconds(zdt.toInstant().getEpochSecond())
+ .setNanos(zdt.toInstant().getNano())
+ .build();
+ }
+
+ public static Timestamp fromEpochNanos(long nanos) {
+ long seconds = nanos / 1000_000_000;
+ int nanosInt = (int) (nanos - seconds * 1000_000_000);
+ int res = nanosInt % 1000_000;
+ return Timestamp.newBuilder()
+ .setSeconds(seconds)
+ .setNanos(nanosInt - res)
+ .build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/main/proto b/oap-server/server-library/library-banyandb-client/src/main/proto
new file mode 160000
index 0000000000..b1c87663e5
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/main/proto
@@ -0,0 +1 @@
+Subproject commit b1c87663e57796402736a5f52dd3b4ca8d981e89
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/AbstractBanyanDBClientTest.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/AbstractBanyanDBClientTest.java
new file mode 100644
index 0000000000..3f59ab12c6
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/AbstractBanyanDBClientTest.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import io.grpc.BindableService;
+import io.grpc.ForwardingServerBuilder;
+import io.grpc.ManagedChannel;
+import io.grpc.Server;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.stub.StreamObserver;
+import io.grpc.util.MutableHandlerRegistry;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.IndexRuleBindingRegistryServiceGrpc;
+import org.apache.skywalking.banyandb.database.v1.IndexRuleRegistryServiceGrpc;
+import org.apache.skywalking.banyandb.database.v1.MeasureRegistryServiceGrpc;
+import org.apache.skywalking.banyandb.database.v1.StreamRegistryServiceGrpc;
+import org.apache.skywalking.banyandb.database.v1.TopNAggregationRegistryServiceGrpc;
+import org.apache.skywalking.library.banyandb.v1.client.util.TimeUtils;
+import java.io.IOException;
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.mockito.AdditionalAnswers.delegatesTo;
+import static org.mockito.Mockito.mock;
+
+public class AbstractBanyanDBClientTest {
+ // play as an in-memory registry
+ protected Map<String, BanyandbDatabase.IndexRuleBinding> indexRuleBindingRegistry;
+
+ private final IndexRuleBindingRegistryServiceGrpc.IndexRuleBindingRegistryServiceImplBase indexRuleBindingServiceImpl =
+ mock(IndexRuleBindingRegistryServiceGrpc.IndexRuleBindingRegistryServiceImplBase.class, delegatesTo(
+ new IndexRuleBindingRegistryServiceGrpc.IndexRuleBindingRegistryServiceImplBase() {
+ @Override
+ public void create(BanyandbDatabase.IndexRuleBindingRegistryServiceCreateRequest request, StreamObserver<BanyandbDatabase.IndexRuleBindingRegistryServiceCreateResponse> responseObserver) {
+ BanyandbDatabase.IndexRuleBinding s = request.getIndexRuleBinding().toBuilder()
+ .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .build();
+ indexRuleBindingRegistry.put(s.getMetadata().getName(), s);
+ responseObserver.onNext(BanyandbDatabase.IndexRuleBindingRegistryServiceCreateResponse.newBuilder().build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void update(BanyandbDatabase.IndexRuleBindingRegistryServiceUpdateRequest request, StreamObserver<BanyandbDatabase.IndexRuleBindingRegistryServiceUpdateResponse> responseObserver) {
+ BanyandbDatabase.IndexRuleBinding s = request.getIndexRuleBinding().toBuilder()
+ .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .build();
+ indexRuleBindingRegistry.put(s.getMetadata().getName(), s);
+ responseObserver.onNext(BanyandbDatabase.IndexRuleBindingRegistryServiceUpdateResponse.newBuilder().build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void delete(BanyandbDatabase.IndexRuleBindingRegistryServiceDeleteRequest request, StreamObserver<BanyandbDatabase.IndexRuleBindingRegistryServiceDeleteResponse> responseObserver) {
+ BanyandbDatabase.IndexRuleBinding oldIndexRuleBinding = indexRuleBindingRegistry.remove(request.getMetadata().getName());
+ responseObserver.onNext(BanyandbDatabase.IndexRuleBindingRegistryServiceDeleteResponse.newBuilder()
+ .setDeleted(oldIndexRuleBinding != null)
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void get(BanyandbDatabase.IndexRuleBindingRegistryServiceGetRequest request, StreamObserver<BanyandbDatabase.IndexRuleBindingRegistryServiceGetResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.IndexRuleBindingRegistryServiceGetResponse.newBuilder()
+ .setIndexRuleBinding(indexRuleBindingRegistry.get(request.getMetadata().getName()))
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void list(BanyandbDatabase.IndexRuleBindingRegistryServiceListRequest request, StreamObserver<BanyandbDatabase.IndexRuleBindingRegistryServiceListResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.IndexRuleBindingRegistryServiceListResponse.newBuilder()
+ .addAllIndexRuleBinding(indexRuleBindingRegistry.values())
+ .build());
+ responseObserver.onCompleted();
+ }
+ }));
+
+ // play as an in-memory registry
+ protected Map<String, BanyandbDatabase.IndexRule> indexRuleRegistry;
+
+ private final IndexRuleRegistryServiceGrpc.IndexRuleRegistryServiceImplBase indexRuleServiceImpl =
+ mock(IndexRuleRegistryServiceGrpc.IndexRuleRegistryServiceImplBase.class, delegatesTo(
+ new IndexRuleRegistryServiceGrpc.IndexRuleRegistryServiceImplBase() {
+ @Override
+ public void create(BanyandbDatabase.IndexRuleRegistryServiceCreateRequest request, StreamObserver<BanyandbDatabase.IndexRuleRegistryServiceCreateResponse> responseObserver) {
+ BanyandbDatabase.IndexRule s = request.getIndexRule().toBuilder().setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .build();
+ indexRuleRegistry.put(s.getMetadata().getName(), s);
+ responseObserver.onNext(BanyandbDatabase.IndexRuleRegistryServiceCreateResponse.newBuilder().build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void update(BanyandbDatabase.IndexRuleRegistryServiceUpdateRequest request, StreamObserver<BanyandbDatabase.IndexRuleRegistryServiceUpdateResponse> responseObserver) {
+ BanyandbDatabase.IndexRule s = request.getIndexRule().toBuilder().setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .build();
+ indexRuleRegistry.put(s.getMetadata().getName(), s);
+ responseObserver.onNext(BanyandbDatabase.IndexRuleRegistryServiceUpdateResponse.newBuilder().build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void delete(BanyandbDatabase.IndexRuleRegistryServiceDeleteRequest request, StreamObserver<BanyandbDatabase.IndexRuleRegistryServiceDeleteResponse> responseObserver) {
+ BanyandbDatabase.IndexRule oldIndexRule = indexRuleRegistry.remove(request.getMetadata().getName());
+ responseObserver.onNext(BanyandbDatabase.IndexRuleRegistryServiceDeleteResponse.newBuilder()
+ .setDeleted(oldIndexRule != null)
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void get(BanyandbDatabase.IndexRuleRegistryServiceGetRequest request, StreamObserver<BanyandbDatabase.IndexRuleRegistryServiceGetResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.IndexRuleRegistryServiceGetResponse.newBuilder()
+ .setIndexRule(indexRuleRegistry.get(request.getMetadata().getName()))
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void list(BanyandbDatabase.IndexRuleRegistryServiceListRequest request, StreamObserver<BanyandbDatabase.IndexRuleRegistryServiceListResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.IndexRuleRegistryServiceListResponse.newBuilder()
+ .addAllIndexRule(indexRuleRegistry.values())
+ .build());
+ responseObserver.onCompleted();
+ }
+ }));
+
+ // stream registry
+ protected Map<String, BanyandbDatabase.Stream> streamRegistry;
+
+ private final StreamRegistryServiceGrpc.StreamRegistryServiceImplBase streamRegistryServiceImpl =
+ mock(StreamRegistryServiceGrpc.StreamRegistryServiceImplBase.class, delegatesTo(
+ new StreamRegistryServiceGrpc.StreamRegistryServiceImplBase() {
+ private final AtomicLong revisionGenerator = new AtomicLong(0);
+
+ @Override
+ public void create(BanyandbDatabase.StreamRegistryServiceCreateRequest request, StreamObserver<BanyandbDatabase.StreamRegistryServiceCreateResponse> responseObserver) {
+ BanyandbDatabase.Stream s = request.getStream().toBuilder()
+ .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .setMetadata(request.getStream().getMetadata().toBuilder()
+ .setModRevision(revisionGenerator.incrementAndGet())
+ .build())
+ .build();
+ streamRegistry.put(s.getMetadata().getName(), s);
+ responseObserver.onNext(BanyandbDatabase.StreamRegistryServiceCreateResponse.newBuilder().setModRevision(s.getMetadata().getModRevision()).build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void update(BanyandbDatabase.StreamRegistryServiceUpdateRequest request, StreamObserver<BanyandbDatabase.StreamRegistryServiceUpdateResponse> responseObserver) {
+ BanyandbDatabase.Stream s = request.getStream().toBuilder()
+ .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .setMetadata(request.getStream().getMetadata().toBuilder()
+ .setModRevision(revisionGenerator.incrementAndGet())
+ .build())
+ .build();
+ streamRegistry.put(s.getMetadata().getName(), s);
+ responseObserver.onNext(BanyandbDatabase.StreamRegistryServiceUpdateResponse.newBuilder().setModRevision(s.getMetadata().getModRevision()).build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void delete(BanyandbDatabase.StreamRegistryServiceDeleteRequest request, StreamObserver<BanyandbDatabase.StreamRegistryServiceDeleteResponse> responseObserver) {
+ BanyandbDatabase.Stream oldStream = streamRegistry.remove(request.getMetadata().getName());
+ responseObserver.onNext(BanyandbDatabase.StreamRegistryServiceDeleteResponse.newBuilder()
+ .setDeleted(oldStream != null)
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void get(BanyandbDatabase.StreamRegistryServiceGetRequest request, StreamObserver<BanyandbDatabase.StreamRegistryServiceGetResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.StreamRegistryServiceGetResponse.newBuilder()
+ .setStream(streamRegistry.get(request.getMetadata().getName()))
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void list(BanyandbDatabase.StreamRegistryServiceListRequest request, StreamObserver<BanyandbDatabase.StreamRegistryServiceListResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.StreamRegistryServiceListResponse.newBuilder()
+ .addAllStream(streamRegistry.values())
+ .build());
+ responseObserver.onCompleted();
+ }
+ }));
+
+ // measure registry
+ protected Map<String, BanyandbDatabase.Measure> measureRegistry;
+
+ private final MeasureRegistryServiceGrpc.MeasureRegistryServiceImplBase measureRegistryServiceImpl =
+ mock(MeasureRegistryServiceGrpc.MeasureRegistryServiceImplBase.class, delegatesTo(
+ new MeasureRegistryServiceGrpc.MeasureRegistryServiceImplBase() {
+ private final AtomicLong revisionGenerator = new AtomicLong(0);
+
+ @Override
+ public void create(BanyandbDatabase.MeasureRegistryServiceCreateRequest request, StreamObserver<BanyandbDatabase.MeasureRegistryServiceCreateResponse> responseObserver) {
+ BanyandbDatabase.Measure s = request.getMeasure().toBuilder()
+ .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .setMetadata(request.getMeasure().getMetadata().toBuilder()
+ .setModRevision(revisionGenerator.incrementAndGet())
+ .build())
+ .build();
+ measureRegistry.put(s.getMetadata().getName(), s);
+ responseObserver.onNext(BanyandbDatabase.MeasureRegistryServiceCreateResponse.newBuilder().setModRevision(s.getMetadata().getModRevision()).build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void update(BanyandbDatabase.MeasureRegistryServiceUpdateRequest request, StreamObserver<BanyandbDatabase.MeasureRegistryServiceUpdateResponse> responseObserver) {
+ BanyandbDatabase.Measure s = request.getMeasure().toBuilder()
+ .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .setMetadata(request.getMeasure().getMetadata().toBuilder()
+ .setModRevision(revisionGenerator.incrementAndGet())
+ .build())
+ .build();
+ measureRegistry.put(s.getMetadata().getName(), s);
+ responseObserver.onNext(BanyandbDatabase.MeasureRegistryServiceUpdateResponse.newBuilder().setModRevision(s.getMetadata().getModRevision()).build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void delete(BanyandbDatabase.MeasureRegistryServiceDeleteRequest request, StreamObserver<BanyandbDatabase.MeasureRegistryServiceDeleteResponse> responseObserver) {
+ BanyandbDatabase.Measure oldMeasure = measureRegistry.remove(request.getMetadata().getName());
+ responseObserver.onNext(BanyandbDatabase.MeasureRegistryServiceDeleteResponse.newBuilder()
+ .setDeleted(oldMeasure != null)
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void get(BanyandbDatabase.MeasureRegistryServiceGetRequest request, StreamObserver<BanyandbDatabase.MeasureRegistryServiceGetResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.MeasureRegistryServiceGetResponse.newBuilder()
+ .setMeasure(measureRegistry.get(request.getMetadata().getName()))
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void list(BanyandbDatabase.MeasureRegistryServiceListRequest request, StreamObserver<BanyandbDatabase.MeasureRegistryServiceListResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.MeasureRegistryServiceListResponse.newBuilder()
+ .addAllMeasure(measureRegistry.values())
+ .build());
+ responseObserver.onCompleted();
+ }
+ }));
+
+ // measure registry
+ protected Map<String, BanyandbDatabase.TopNAggregation> topNAggregationRegistry;
+
+ private final TopNAggregationRegistryServiceGrpc.TopNAggregationRegistryServiceImplBase topNAggregationRegistryServiceImpl =
+ mock(TopNAggregationRegistryServiceGrpc.TopNAggregationRegistryServiceImplBase.class, delegatesTo(
+ new TopNAggregationRegistryServiceGrpc.TopNAggregationRegistryServiceImplBase() {
+ @Override
+ public void create(BanyandbDatabase.TopNAggregationRegistryServiceCreateRequest request, StreamObserver<BanyandbDatabase.TopNAggregationRegistryServiceCreateResponse> responseObserver) {
+ BanyandbDatabase.TopNAggregation aggr = request.getTopNAggregation().toBuilder()
+ .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .build();
+ topNAggregationRegistry.put(aggr.getMetadata().getName(), aggr);
+ responseObserver.onNext(BanyandbDatabase.TopNAggregationRegistryServiceCreateResponse.newBuilder().build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void update(BanyandbDatabase.TopNAggregationRegistryServiceUpdateRequest request, StreamObserver<BanyandbDatabase.TopNAggregationRegistryServiceUpdateResponse> responseObserver) {
+ BanyandbDatabase.TopNAggregation aggr = request.getTopNAggregation().toBuilder()
+ .setUpdatedAt(TimeUtils.buildTimestamp(ZonedDateTime.now()))
+ .build();
+ topNAggregationRegistry.put(aggr.getMetadata().getName(), aggr);
+ responseObserver.onNext(BanyandbDatabase.TopNAggregationRegistryServiceUpdateResponse.newBuilder().build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void delete(BanyandbDatabase.TopNAggregationRegistryServiceDeleteRequest request, StreamObserver<BanyandbDatabase.TopNAggregationRegistryServiceDeleteResponse> responseObserver) {
+ BanyandbDatabase.TopNAggregation oldMeasure = topNAggregationRegistry.remove(request.getMetadata().getName());
+ responseObserver.onNext(BanyandbDatabase.TopNAggregationRegistryServiceDeleteResponse.newBuilder()
+ .setDeleted(oldMeasure != null)
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void get(BanyandbDatabase.TopNAggregationRegistryServiceGetRequest request, StreamObserver<BanyandbDatabase.TopNAggregationRegistryServiceGetResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.TopNAggregationRegistryServiceGetResponse.newBuilder()
+ .setTopNAggregation(topNAggregationRegistry.get(request.getMetadata().getName()))
+ .build());
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void list(BanyandbDatabase.TopNAggregationRegistryServiceListRequest request, StreamObserver<BanyandbDatabase.TopNAggregationRegistryServiceListResponse> responseObserver) {
+ responseObserver.onNext(BanyandbDatabase.TopNAggregationRegistryServiceListResponse.newBuilder()
+ .addAllTopNAggregation(topNAggregationRegistry.values())
+ .build());
+ responseObserver.onCompleted();
+ }
+ }));
+
+ protected final MutableHandlerRegistry serviceRegistry = new MutableHandlerRegistry();
+
+ protected BanyanDBClient client;
+
+ protected ManagedChannel channel;
+
+ protected void setUp(SetupFunction... setUpFunctions) throws IOException {
+ indexRuleRegistry = new HashMap<>();
+ serviceRegistry.addService(indexRuleServiceImpl);
+
+ indexRuleBindingRegistry = new HashMap<>();
+ serviceRegistry.addService(indexRuleBindingServiceImpl);
+ indexRuleBindingRegistry = new HashMap<>();
+
+ // Generate a unique in-process server name.
+ String serverName = InProcessServerBuilder.generateName();
+
+ // Create a server, add service, start, and register for automatic graceful shutdown.
+ InProcessServerBuilder serverBuilder = InProcessServerBuilder
+ .forName(serverName).directExecutor()
+ .fallbackHandlerRegistry(serviceRegistry);
+ for (final SetupFunction func : setUpFunctions) {
+ func.apply(serverBuilder);
+ }
+ final Server s = serverBuilder.build();
+ s.start();
+ this.channel = InProcessChannelBuilder.forName(serverName).directExecutor().build();
+
+ client = new BanyanDBClient(String.format("127.0.0.1:%d", s.getPort()));
+ client.connect(this.channel);
+ }
+
+ protected interface SetupFunction {
+ void apply(ForwardingServerBuilder<?> builder);
+ }
+
+ protected SetupFunction bindStreamRegistry() {
+ return b -> {
+ AbstractBanyanDBClientTest.this.streamRegistry = new HashMap<>();
+ serviceRegistry.addService(streamRegistryServiceImpl);
+ };
+ }
+
+ protected SetupFunction bindService(final BindableService bindableService) {
+ return b -> serviceRegistry.addService(bindableService);
+ }
+
+ protected SetupFunction bindMeasureRegistry() {
+ return b -> {
+ AbstractBanyanDBClientTest.this.measureRegistry = new HashMap<>();
+ serviceRegistry.addService(measureRegistryServiceImpl);
+ };
+ }
+
+ protected SetupFunction bindTopNAggregationRegistry() {
+ return b -> {
+ AbstractBanyanDBClientTest.this.topNAggregationRegistry = new HashMap<>();
+ serviceRegistry.addService(topNAggregationRegistryServiceImpl);
+ };
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/BanyanDBClientTestCI.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/BanyanDBClientTestCI.java
new file mode 100644
index 0000000000..fdd826ca01
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/BanyanDBClientTestCI.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.oap.server.library.it.ITVersions;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.DockerImageName;
+import java.io.IOException;
+
+@Slf4j
+@Testcontainers
+public class BanyanDBClientTestCI {
+ private static final String REGISTRY = "ghcr.io";
+ private static final String IMAGE_NAME = "apache/skywalking-banyandb";
+ private static final String TAG = ITVersions.get("SW_BANYANDB_COMMIT");
+
+ private static final String IMAGE = REGISTRY + "/" + IMAGE_NAME + ":" + TAG;
+
+ protected static final int GRPC_PORT = 17912;
+ protected static final int HTTP_PORT = 17913;
+
+ @Container
+ public GenericContainer<?> banyanDB = new GenericContainer<>(
+ DockerImageName.parse(IMAGE))
+ .withCommand("standalone", "--stream-root-path", "/tmp/banyandb-stream-data",
+ "--measure-root-path", "/tmp/banyand-measure-data")
+ .withExposedPorts(GRPC_PORT, HTTP_PORT)
+ .waitingFor(Wait.forHttp("/api/healthz").forPort(HTTP_PORT));
+
+ protected BanyanDBClient client;
+
+ protected void setUpConnection() throws IOException {
+ log.info("create BanyanDB client and try to connect");
+ client = new BanyanDBClient(String.format("%s:%d", banyanDB.getHost(), banyanDB.getMappedPort(GRPC_PORT)));
+ client.connect();
+ }
+
+ protected void closeClient() throws IOException {
+ if (this.client != null) {
+ this.client.close();
+ }
+ }
+
+ protected BanyandbCommon.Group buildStreamGroup() {
+ return BanyandbCommon.Group.newBuilder().setMetadata(BanyandbCommon.Metadata.newBuilder().setName("sw_record"))
+ .setCatalog(BanyandbCommon.Catalog.CATALOG_STREAM)
+ .setResourceOpts(BanyandbCommon.ResourceOpts.newBuilder()
+ .setShardNum(2)
+ .setSegmentInterval(
+ BanyandbCommon.IntervalRule.newBuilder()
+ .setUnit(
+ BanyandbCommon.IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 1))
+ .setTtl(
+ BanyandbCommon.IntervalRule.newBuilder()
+ .setUnit(
+ BanyandbCommon.IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 3)))
+ .build();
+ }
+
+ protected BanyandbCommon.Group buildMeasureGroup() {
+ return BanyandbCommon.Group.newBuilder().setMetadata(BanyandbCommon.Metadata.newBuilder().setName("sw_metric"))
+ .setCatalog(BanyandbCommon.Catalog.CATALOG_MEASURE)
+ .setResourceOpts(BanyandbCommon.ResourceOpts.newBuilder()
+ .setShardNum(2)
+ .setSegmentInterval(
+ BanyandbCommon.IntervalRule.newBuilder()
+ .setUnit(
+ BanyandbCommon.IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 1))
+ .setTtl(
+ BanyandbCommon.IntervalRule.newBuilder()
+ .setUnit(
+ BanyandbCommon.IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 7)))
+ .build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBAuthTest.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBAuthTest.java
new file mode 100644
index 0000000000..d2d6883607
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBAuthTest.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.UnauthenticatedException;
+import org.apache.skywalking.oap.server.library.it.ITVersions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+import org.testcontainers.utility.MountableFile;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
+
+public class ITBanyanDBAuthTest extends BanyanDBClientTestCI {
+ private static final String REGISTRY = "ghcr.io";
+ private static final String IMAGE_NAME = "apache/skywalking-banyandb";
+ private static final String TAG = ITVersions.get("SW_BANYANDB_COMMIT");
+
+ private static final String IMAGE = REGISTRY + "/" + IMAGE_NAME + ":" + TAG;
+
+ protected static final int GRPC_PORT = 17912;
+ protected static final int HTTP_PORT = 17913;
+
+ public static GenericContainer<?> DB;
+
+ @BeforeAll
+ public static void init() throws Exception {
+ // Step 1: prepare config file with 0600 permissions
+ Path tempConfigPath = Files.createTempFile("bydb_server_config", ".yaml");
+ Files.write(
+ tempConfigPath, Files.readAllBytes(
+ Paths.get(ITBanyanDBAuthTest.class.getClassLoader().getResource("config.yaml").toURI()))
+ );
+ Files.setPosixFilePermissions(tempConfigPath, PosixFilePermissions.fromString("rw-------"));
+
+ // Step 2: create container
+ DB = new GenericContainer<>(DockerImageName.parse(IMAGE))
+ .withCopyFileToContainer(
+ MountableFile.forHostPath(tempConfigPath),
+ "/tmp/bydb_server_config.yaml"
+ )
+ .withCommand(
+ "standalone",
+ "--auth-config-file", "/tmp/bydb_server_config.yaml"
+ )
+ .withExposedPorts(GRPC_PORT, HTTP_PORT)
+ .waitingFor(Wait.forHttp("/api/healthz").forPort(HTTP_PORT));
+ DB.start();
+ }
+
+ @Test
+ public void testAuthWithCorrect() throws IOException {
+ BanyanDBClient client = createClient("admin", "123456");
+ client.connect();
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ // get api version
+ client.getAPIVersion();
+ // list all groups
+ List<BanyandbCommon.Group> groupList = client.findGroups();
+ assertEquals(0, groupList.size());
+ });
+ client.close();
+ }
+
+ @Test
+ public void testAuthWithWrong() throws IOException {
+ BanyanDBClient client = createClient("admin", "123456" + "wrong");
+ client.connect();
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ assertThrows(UnauthenticatedException.class, client::getAPIVersion);
+ });
+ client.close();
+ }
+
+ private BanyanDBClient createClient(String username, String password) {
+ Options options = new Options();
+ options.setUsername(username);
+ options.setPassword(password);
+ String url = String.format("%s:%d", DB.getHost(), DB.getMappedPort(GRPC_PORT));
+ return new BanyanDBClient(new String[] {url}, options);
+ }
+}
\ No newline at end of file
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBCommonTests.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBCommonTests.java
new file mode 100644
index 0000000000..11b9abb242
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBCommonTests.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.common.base.Strings;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import java.io.IOException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class ITBanyanDBCommonTests extends BanyanDBClientTestCI {
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ super.setUpConnection();
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void test_GetAPIVersion() throws BanyanDBException {
+ BanyandbCommon.APIVersion version = this.client.getAPIVersion();
+ assertEquals("0.9", version.getVersion());
+ assertFalse(Strings.isNullOrEmpty(version.getRevision()));
+ }
+
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBMeasureQueryTests.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBMeasureQueryTests.java
new file mode 100644
index 0000000000..faeb527f97
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBMeasureQueryTests.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import io.grpc.stub.StreamObserver;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Group;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Catalog;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.IntervalRule;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.ResourceOpts;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Metadata;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.CompressionMethod;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.EncodingMethod;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Entity;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.FieldSpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.FieldType;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Measure;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagFamilySpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagSpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagType;
+import org.apache.skywalking.banyandb.measure.v1.BanyandbMeasure;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.Duration;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
+
+public class ITBanyanDBMeasureQueryTests extends BanyanDBClientTestCI {
+
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ super.setUpConnection();
+ Group expectedGroup = buildGroup();
+ client.define(expectedGroup);
+ assertNotNull(expectedGroup);
+ Measure expectedMeasure = buildMeasure();
+ client.define(expectedMeasure);
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void testMeasureQuery() throws BanyanDBException, ExecutionException, InterruptedException, TimeoutException {
+ // try to write a metrics
+ Instant now = Instant.now();
+ Instant begin = now.minus(15, ChronoUnit.MINUTES);
+
+ MeasureWrite measureWrite = client.createMeasureWrite("sw_metric", "service_cpm_minute", now.toEpochMilli());
+ measureWrite.tag("storage-only", "entity_id", TagAndValue.stringTagValue("entity_1"))
+ .field("total", TagAndValue.longFieldValue(100))
+ .field("value", TagAndValue.longFieldValue(1));
+ StreamObserver<BanyandbMeasure.WriteRequest> writeObserver
+ = client.getMeasureServiceStub().write(new StreamObserver<BanyandbMeasure.WriteResponse>() {
+ @Override
+ public void onNext(BanyandbMeasure.WriteResponse writeResponse) {
+ assertEquals(BanyandbModel.Status.STATUS_SUCCEED.name(), writeResponse.getStatus());
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ fail("write failed: " + throwable.getMessage());
+ }
+
+ @Override
+ public void onCompleted() {
+
+ }
+ });
+ try {
+ writeObserver.onNext(measureWrite.build());
+
+ } finally {
+ writeObserver.onCompleted();
+ }
+
+ MeasureQuery query = new MeasureQuery(
+ Lists.newArrayList("sw_metric"), "service_cpm_minute",
+ new TimestampRange(begin.toEpochMilli(), now.plus(1, ChronoUnit.MINUTES).toEpochMilli()),
+ ImmutableMap.of("entity_id", "storage-only"),
+ ImmutableSet.of("total")
+ ); // fields
+
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ MeasureQueryResponse resp = client.query(query);
+ assertNotNull(resp);
+ assertEquals(1, resp.size());
+ });
+ }
+
+ private Group buildGroup() {
+ return Group.newBuilder().setMetadata(Metadata.newBuilder().setName("sw_metric"))
+ .setCatalog(Catalog.CATALOG_MEASURE)
+ .setResourceOpts(ResourceOpts.newBuilder()
+ .setShardNum(2)
+ .setSegmentInterval(
+ IntervalRule.newBuilder()
+ .setUnit(
+ IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 1))
+ .setTtl(
+ IntervalRule.newBuilder()
+ .setUnit(
+ IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 7)))
+ .build();
+ }
+
+ private Measure buildMeasure() {
+ Measure.Builder builder = Measure.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup("sw_metric")
+ .setName("service_cpm_minute"))
+ .setInterval(Duration.ofMinutes(1).format())
+ .setEntity(Entity.newBuilder().addTagNames("entity_id"))
+ .addTagFamilies(
+ TagFamilySpec.newBuilder()
+ .setName("default")
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("entity_id")
+ .setType(
+ TagType.TAG_TYPE_STRING))
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("scope")
+ .setType(
+ TagType.TAG_TYPE_STRING)))
+ .addFields(
+ FieldSpec.newBuilder()
+ .setName("total")
+ .setFieldType(
+ FieldType.FIELD_TYPE_INT)
+ .setCompressionMethod(
+ CompressionMethod.COMPRESSION_METHOD_ZSTD)
+ .setEncodingMethod(
+ EncodingMethod.ENCODING_METHOD_GORILLA))
+ .addFields(
+ FieldSpec.newBuilder()
+ .setName("value")
+ .setFieldType(
+ FieldType.FIELD_TYPE_INT)
+ .setCompressionMethod(
+ CompressionMethod.COMPRESSION_METHOD_ZSTD)
+ .setEncodingMethod(
+ EncodingMethod.ENCODING_METHOD_GORILLA));
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBPropertyTests.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBPropertyTests.java
new file mode 100644
index 0000000000..1a98215717
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBPropertyTests.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel.Tag;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel.TagValue;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel.Str;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Group;
+import org.apache.skywalking.banyandb.property.v1.BanyandbProperty.Property;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Metadata;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagSpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
+
+public class ITBanyanDBPropertyTests extends BanyanDBClientTestCI {
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ super.setUpConnection();
+ Group expectedGroup =
+ Group.newBuilder().setMetadata(Metadata.newBuilder()
+ .setName("default"))
+ .setCatalog(BanyandbCommon.Catalog.CATALOG_PROPERTY)
+ .setResourceOpts(BanyandbCommon.ResourceOpts.newBuilder()
+ .setShardNum(2))
+ .build();
+ client.define(expectedGroup);
+ assertNotNull(expectedGroup);
+ org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property expectedProperty =
+ org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property.newBuilder()
+ .setMetadata(
+ Metadata.newBuilder()
+ .setGroup("default")
+ .setName("sw")
+ .build())
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("name")
+ .setType(
+ TagType.TAG_TYPE_STRING))
+ .build();
+ client.define(expectedProperty);
+ assertNotNull(expectedProperty);
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void test_PropertyCreateAndGet() throws BanyanDBException {
+ Property property = buildProperty("default", "sw", "ui_template").toBuilder().addTags(
+ Tag.newBuilder().setKey("name").setValue(
+ TagValue.newBuilder().setStr(Str.newBuilder().setValue("hello")))).build();
+ PropertyStore store = new PropertyStore(client.getChannel());
+ assertTrue(store.apply(property).getCreated());
+
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
+ .addGroups("default")
+ .setName("sw")
+ .addIds("ui_template")
+ .build());
+ assertEquals(1, resp.getPropertiesCount());
+ Property gotProperty = resp.getProperties(0);
+ assertNotNull(gotProperty);
+ assertEquals(property.getTagsList(), gotProperty.getTagsList());
+ });
+ }
+
+ @Test
+ public void test_PropertyCreateDeleteAndGet() throws BanyanDBException {
+ Property property = buildProperty("default", "sw", "ui_template").toBuilder().addTags(
+ Tag.newBuilder().setKey("name").setValue(
+ TagValue.newBuilder().setStr(Str.newBuilder().setValue("hello")))).build();
+ PropertyStore store = new PropertyStore(client.getChannel());
+ assertTrue(store.apply(property).getCreated());
+
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
+ .addGroups("default")
+ .setName("sw")
+ .addIds("ui_template")
+ .build());
+ assertEquals(1, resp.getPropertiesCount());
+ Property gotProperty = resp.getProperties(0);
+ assertNotNull(gotProperty);
+ assertEquals(property.getTagsList(), gotProperty.getTagsList());
+ });
+ BanyandbProperty.DeleteResponse result = store.delete("default", "sw", "ui_template");
+ assertTrue(result.getDeleted());
+ BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
+ .addGroups("default")
+ .setName("sw")
+ .addIds("ui_template")
+ .build());
+ assertEquals(0, resp.getPropertiesCount());
+ }
+
+ @Test
+ public void test_PropertyCreateUpdateAndGet() throws BanyanDBException {
+ Property property1 = buildProperty("default", "sw", "ui_template").toBuilder().addTags(
+ Tag.newBuilder().setKey("name").setValue(
+ TagValue.newBuilder().setStr(Str.newBuilder().setValue("hello")))).build();
+ PropertyStore store = new PropertyStore(client.getChannel());
+ assertTrue(store.apply(property1).getCreated());
+
+ Property property2 = buildProperty("default", "sw", "ui_template").toBuilder().addTags(
+ Tag.newBuilder().setKey("name").setValue(
+ TagValue.newBuilder().setStr(Str.newBuilder().setValue("word")))).build();
+ assertFalse(store.apply(property2).getCreated());
+
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
+ .addGroups("default")
+ .setName("sw")
+ .addIds("ui_template")
+ .build());
+ assertEquals(1, resp.getPropertiesCount());
+ Property gotProperty = resp.getProperties(0);
+ assertNotNull(gotProperty);
+ assertEquals(property2.getTagsList(), gotProperty.getTagsList());
+ });
+ }
+
+ @Test
+ public void test_PropertyList() throws BanyanDBException {
+ Property property = buildProperty("default", "sw", "id1").toBuilder().addTags(
+ Tag.newBuilder().setKey("name").setValue(
+ TagValue.newBuilder().setStr(Str.newBuilder().setValue("bar")))).build();
+ PropertyStore store = new PropertyStore(client.getChannel());
+ assertTrue(store.apply(property).getCreated());
+ property = buildProperty("default", "sw", "id2").toBuilder().addTags(
+ Tag.newBuilder().setKey("name").setValue(
+ TagValue.newBuilder().setStr(Str.newBuilder().setValue("foo")))).build();
+ assertTrue(store.apply(property).getCreated());
+
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ client.query(new PropertyQuery(Lists.newArrayList("default"), "sw", ImmutableSet.of("name")).build());
+ BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
+ .addGroups("default")
+ .setName("sw")
+ .build());
+ assertEquals(2, resp.getPropertiesCount());
+ });
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
+ .addGroups("default")
+ .setName("sw")
+ .addIds("id1")
+ .addIds("id2")
+ .build());
+ assertEquals(2, resp.getPropertiesCount());
+ });
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
+ .addGroups("default")
+ .setName("sw")
+ .addIds("id2")
+ .build());
+ assertEquals(1, resp.getPropertiesCount());
+ });
+ }
+
+ @Test
+ public void test_PropertyQuery() throws BanyanDBException {
+ Property property = buildProperty("default", "sw", "id1").toBuilder().addTags(
+ Tag.newBuilder().setKey("name").setValue(
+ TagValue.newBuilder().setStr(Str.newBuilder().setValue("bar")))).build();
+ PropertyStore store = new PropertyStore(client.getChannel());
+ assertTrue(store.apply(property).getCreated());
+ property = buildProperty("default", "sw", "id2").toBuilder().addTags(
+ Tag.newBuilder().setKey("name").setValue(
+ TagValue.newBuilder().setStr(Str.newBuilder().setValue("foo")))).build();
+ assertTrue(store.apply(property).getCreated());
+
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ BanyandbProperty.QueryResponse resp = client.query(new PropertyQuery(Lists.newArrayList("default"), "sw", ImmutableSet.of("name")).build());
+ assertEquals(2, resp.getPropertiesCount());
+ });
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ PropertyQuery pQuery = new PropertyQuery(Lists.newArrayList("default"), "sw", ImmutableSet.of("name"));
+ pQuery.criteria(PairQueryCondition.StringQueryCondition.eq("name", "foo"));
+ BanyandbProperty.QueryResponse resp = client.query(pQuery.build());
+ assertEquals(1, resp.getPropertiesCount());
+ });
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ PropertyQuery pQuery = new PropertyQuery(Lists.newArrayList("default"), "sw", ImmutableSet.of("name"));
+ pQuery.criteria(Or.create(PairQueryCondition.StringQueryCondition.eq("name", "foo"),
+ PairQueryCondition.StringQueryCondition.eq("name", "bar")));
+ BanyandbProperty.QueryResponse resp = client.query(pQuery.build());
+ assertEquals(2, resp.getPropertiesCount());
+ });
+ }
+
+ private Property buildProperty(String group, String name, String id) {
+ Property.Builder builder = Property.newBuilder()
+ .setMetadata(
+ Metadata.newBuilder()
+ .setGroup(
+ group)
+ .setName(
+ name).build())
+ .setId(id);
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBStreamQueryTests.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBStreamQueryTests.java
new file mode 100644
index 0000000000..bbc1688d4c
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITBanyanDBStreamQueryTests.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import io.grpc.stub.StreamObserver;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Group;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Catalog;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.IntervalRule;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.ResourceOpts;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Metadata;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Entity;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagFamilySpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagSpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagType;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Stream;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.IndexRule;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.IndexRuleBinding;
+import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
+import org.apache.skywalking.banyandb.stream.v1.BanyandbStream;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.util.TimeUtils;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.skywalking.library.banyandb.v1.client.BanyanDBClient.DEFAULT_EXPIRE_AT;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
+
+public class ITBanyanDBStreamQueryTests extends BanyanDBClientTestCI {
+
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ this.setUpConnection();
+ this.client.define(buildGroup());
+ this.client.define(buildStream());
+ this.client.define(buildIndexRule());
+ this.client.define(buildIndexRuleBinding());
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void testStreamQuery_TraceID() throws BanyanDBException, ExecutionException, InterruptedException, TimeoutException {
+ // try to write a trace
+ String segmentId = "1231.dfd.123123ssf";
+ String traceId = "trace_id-xxfff.111323";
+ String serviceId = "webapp_id";
+ String serviceInstanceId = "10.0.0.1_id";
+ String endpointId = "home_id";
+ long latency = 200;
+ long state = 1;
+ Instant now = Instant.now();
+ byte[] byteData = new byte[] {14};
+ String broker = "172.16.10.129:9092";
+ String topic = "topic_1";
+ String queue = "queue_2";
+ String httpStatusCode = "200";
+ String dbType = "SQL";
+ String dbInstance = "127.0.0.1:3306";
+
+ StreamWrite streamWrite = client.createStreamWrite("sw_record", "record", segmentId)
+ .tag("storage-only", "data_binary", Value.binaryTagValue(byteData))
+ .tag("searchable", "trace_id", Value.stringTagValue(traceId)) // 0
+ .tag("searchable", "state", Value.longTagValue(state)) // 1
+ .tag("searchable", "service_id", Value.stringTagValue(serviceId)) // 2
+ .tag(
+ "searchable", "service_instance_id",
+ Value.stringTagValue(serviceInstanceId)
+ ) // 3
+ .tag("searchable", "endpoint_id", Value.stringTagValue(endpointId)) // 4
+ .tag("searchable", "duration", Value.longTagValue(latency)) // 5
+ .tag("searchable", "http.method", Value.stringTagValue(null)) // 6
+ .tag("searchable", "status_code", Value.stringTagValue(httpStatusCode)) // 7
+ .tag("searchable", "db.type", Value.stringTagValue(dbType)) // 8
+ .tag("searchable", "db.instance", Value.stringTagValue(dbInstance)) // 9
+ .tag("searchable", "mq.broker", Value.stringTagValue(broker)) // 10
+ .tag("searchable", "mq.topic", Value.stringTagValue(topic)) // 11
+ .tag("searchable", "mq.queue", Value.stringTagValue(queue)); // 12
+ streamWrite.setTimestamp(now.toEpochMilli());
+ StreamObserver<BanyandbStream.WriteRequest> writeObserver
+ = client.getStreamServiceStub().write(new StreamObserver<BanyandbStream.WriteResponse>() {
+ @Override
+ public void onNext(BanyandbStream.WriteResponse writeResponse) {
+ assertEquals(BanyandbModel.Status.STATUS_SUCCEED.name(), writeResponse.getStatus());
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ fail("write failed: " + throwable.getMessage());
+ }
+
+ @Override
+ public void onCompleted() {
+ }
+ });
+ try {
+ writeObserver.onNext(streamWrite.build());
+
+ } finally {
+ writeObserver.onCompleted();
+ }
+
+ StreamQuery query = new StreamQuery(
+ Lists.newArrayList("sw_record"), "record",
+ ImmutableMap.of(
+ "state", "searchable", "duration", "searchable", "trace_id", "searchable", "data_binary",
+ "storage-only"
+ )
+ );
+ query.and(PairQueryCondition.StringQueryCondition.eq("trace_id", traceId));
+ query.setOrderBy(new AbstractQuery.OrderBy(AbstractQuery.Sort.DESC));
+ await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+ StreamQueryResponse resp = client.query(query);
+ assertNotNull(resp);
+ assertEquals(1, resp.size());
+ assertEquals(latency, (Number) resp.getElements().get(0).getTagValue("duration"));
+ assertEquals(traceId, resp.getElements().get(0).getTagValue("trace_id"));
+ });
+ }
+
+ private Group buildGroup() {
+ return Group.newBuilder().setMetadata(Metadata.newBuilder().setName("sw_record"))
+ .setCatalog(Catalog.CATALOG_STREAM)
+ .setResourceOpts(ResourceOpts.newBuilder()
+ .setShardNum(2)
+ .setSegmentInterval(
+ IntervalRule.newBuilder()
+ .setUnit(
+ IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 1))
+ .setTtl(
+ IntervalRule.newBuilder()
+ .setUnit(
+ IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 3)))
+ .build();
+ }
+
+ private Stream buildStream() {
+ Stream.Builder builder = Stream.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup("sw_record")
+ .setName("record"))
+ .setEntity(Entity.newBuilder().addAllTagNames(
+ Arrays.asList("service_id", "service_instance_id", "state")))
+ .addTagFamilies(TagFamilySpec.newBuilder()
+ .setName("data")
+ .addTags(TagSpec.newBuilder()
+ .setName("data_binary")
+ .setType(
+ TagType.TAG_TYPE_DATA_BINARY)))
+ .addTagFamilies(TagFamilySpec.newBuilder()
+ .setName("searchable")
+ .addTags(TagSpec.newBuilder()
+ .setName("trace_id")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("state")
+ .setType(TagType.TAG_TYPE_INT))
+ .addTags(TagSpec.newBuilder()
+ .setName("service_id")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("service_instance_id")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("endpoint_id")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("duration")
+ .setType(TagType.TAG_TYPE_INT))
+ .addTags(TagSpec.newBuilder()
+ .setName("http.method")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("status_code")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("db.type")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("db.instance")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("mq.broker")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("mq.topic")
+ .setType(TagType.TAG_TYPE_STRING))
+ .addTags(TagSpec.newBuilder()
+ .setName("mq.queue")
+ .setType(TagType.TAG_TYPE_STRING)));
+ return builder.build();
+ }
+
+ private IndexRule buildIndexRule() {
+ IndexRule.Builder builder = IndexRule.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup("sw_record")
+ .setName("trace_id"))
+ .addTags("trace_id")
+ .setType(IndexRule.Type.TYPE_INVERTED);
+ return builder.build();
+ }
+
+ private IndexRuleBinding buildIndexRuleBinding() {
+ IndexRuleBinding.Builder builder = IndexRuleBinding.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup("sw_record")
+ .setName("trace_binding"))
+ .setSubject(BanyandbDatabase.Subject.newBuilder()
+ .setCatalog(
+ Catalog.CATALOG_STREAM)
+ .setName("trace"))
+ .addAllRules(
+ Arrays.asList("trace_id"))
+ .setBeginAt(TimeUtils.buildTimestamp(
+ ZonedDateTime.of(
+ 2024, 1, 1, 0, 0, 0, 0,
+ ZoneOffset.UTC
+ )))
+ .setExpireAt(TimeUtils.buildTimestamp(DEFAULT_EXPIRE_AT));
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITGroupMetadataRegistryTest.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITGroupMetadataRegistryTest.java
new file mode 100644
index 0000000000..3b3155f774
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITGroupMetadataRegistryTest.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Group;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Catalog;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.IntervalRule;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.ResourceOpts;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Metadata;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.LifecycleStage;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ITGroupMetadataRegistryTest extends BanyanDBClientTestCI {
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ super.setUpConnection();
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void testGroupRegistry_createAndGet() throws BanyanDBException {
+ Group expectedGroup = buildGroup();
+ this.client.define(expectedGroup);
+ Group actualGroup = client.findGroup("sw_metric");
+ assertNotNull(actualGroup);
+ assertNotNull(actualGroup.getUpdatedAt());
+ actualGroup = actualGroup.toBuilder().setMetadata(actualGroup.getMetadata().toBuilder().clearModRevision().clearCreateRevision()).build();
+ assertEquals(expectedGroup, actualGroup);
+ }
+
+ @Test
+ public void testGroupRegistry_createAndUpdate() throws BanyanDBException {
+ this.client.define(buildGroup());
+ Group beforeGroup = client.findGroup("sw_metric");
+ assertNotNull(beforeGroup);
+ assertNotNull(beforeGroup.getUpdatedAt());
+ Group updatedGroup = beforeGroup.toBuilder()
+ .setResourceOpts(beforeGroup.getResourceOpts()
+ .toBuilder()
+ .setTtl(IntervalRule.newBuilder()
+ .setUnit(
+ IntervalRule.Unit.UNIT_DAY)
+ .setNum(3)))
+ .build();
+ this.client.update(updatedGroup);
+ Group afterGroup = client.findGroup("sw_metric");
+ updatedGroup = updatedGroup.toBuilder().setMetadata(updatedGroup.getMetadata().toBuilder().clearModRevision()).build();
+ afterGroup = afterGroup.toBuilder().setMetadata(afterGroup.getMetadata().toBuilder().clearModRevision()).build();
+ assertNotNull(afterGroup);
+ assertNotNull(afterGroup.getUpdatedAt());
+ assertEquals(updatedGroup, afterGroup);
+ }
+
+ @Test
+ public void testGroupRegistry_createAndList() throws BanyanDBException {
+ Group expectedGroup = buildGroup();
+ this.client.define(buildGroup());
+ List<Group> actualGroups = client.findGroups();
+ assertNotNull(actualGroups);
+ assertEquals(1, actualGroups.size());
+ Group actualGroup = actualGroups.get(0);
+ actualGroup = actualGroup.toBuilder().setMetadata(actualGroup.getMetadata().toBuilder().clearModRevision().clearCreateRevision()).build();
+ assertEquals(expectedGroup, actualGroup);
+ }
+
+ @Test
+ public void testGroupRegistry_createAndDelete() throws BanyanDBException {
+ this.client.define(buildGroup());
+ boolean deleted = this.client.deleteGroup("sw_metric");
+ assertTrue(deleted);
+ assertNull(client.findGroup("sw_metric"));
+ }
+
+ @Test
+ public void testGroupRegistry_hotWarmCold() throws BanyanDBException {
+ Group g = Group.newBuilder().setMetadata(Metadata.newBuilder().setName("sw_record"))
+ .setCatalog(Catalog.CATALOG_STREAM)
+ .setResourceOpts(ResourceOpts.newBuilder()
+ .setShardNum(3)
+ .setSegmentInterval(
+ IntervalRule.newBuilder()
+ .setUnit(IntervalRule.Unit.UNIT_DAY)
+ .setNum(1))
+ .setTtl(
+ IntervalRule.newBuilder()
+ .setUnit(IntervalRule.Unit.UNIT_DAY)
+ .setNum(3))
+ .addStages(LifecycleStage.newBuilder()
+ .setName("warm")
+ .setShardNum(2)
+ .setSegmentInterval(IntervalRule.newBuilder()
+ .setUnit(IntervalRule.Unit.UNIT_DAY)
+ .setNum(1))
+ .setTtl(IntervalRule.newBuilder()
+ .setUnit(IntervalRule.Unit.UNIT_DAY)
+ .setNum(7))
+ .setNodeSelector("hdd-nodes")
+ .build())
+ .addStages(LifecycleStage.newBuilder()
+ .setName("cold")
+ .setShardNum(1)
+ .setSegmentInterval(IntervalRule.newBuilder()
+ .setUnit(IntervalRule.Unit.UNIT_DAY)
+ .setNum(7))
+ .setTtl(IntervalRule.newBuilder()
+ .setUnit(IntervalRule.Unit.UNIT_DAY)
+ .setNum(30))
+ .setNodeSelector("archive-nodes")
+ .setClose(true)
+ .build()))
+ .build();
+ this.client.define(g);
+ Group actualGroup = client.findGroup("sw_record");
+ assertNotNull(actualGroup);
+ assertNotNull(actualGroup.getUpdatedAt());
+
+ // Verify group exists
+ assertNotNull(actualGroup);
+
+ // Verify basic metadata
+ assertEquals("sw_record", actualGroup.getMetadata().getName());
+ assertEquals(Catalog.CATALOG_STREAM, actualGroup.getCatalog());
+
+ // Verify resource options
+ ResourceOpts actualOpts = actualGroup.getResourceOpts();
+ assertEquals(3, actualOpts.getShardNum());
+ assertEquals(IntervalRule.Unit.UNIT_DAY, actualOpts.getSegmentInterval().getUnit());
+ assertEquals(1, actualOpts.getSegmentInterval().getNum());
+ assertEquals(IntervalRule.Unit.UNIT_DAY, actualOpts.getTtl().getUnit());
+ assertEquals(3, actualOpts.getTtl().getNum());
+
+ // Verify stages (should have 2 stages: warm and cold)
+ assertEquals(2, actualOpts.getStagesCount());
+
+ // Verify warm stage
+ LifecycleStage warmStage = actualOpts.getStages(0);
+ assertEquals("warm", warmStage.getName());
+ assertEquals(2, warmStage.getShardNum());
+ assertEquals(1, warmStage.getSegmentInterval().getNum());
+ assertEquals(IntervalRule.Unit.UNIT_DAY, warmStage.getSegmentInterval().getUnit());
+ assertEquals(7, warmStage.getTtl().getNum());
+ assertEquals("hdd-nodes", warmStage.getNodeSelector());
+
+ // Verify cold stage
+ LifecycleStage coldStage = actualOpts.getStages(1);
+ assertEquals("cold", coldStage.getName());
+ assertEquals(1, coldStage.getShardNum());
+ assertEquals(7, coldStage.getSegmentInterval().getNum());
+ assertEquals(30, coldStage.getTtl().getNum());
+ assertEquals("archive-nodes", coldStage.getNodeSelector());
+ assertTrue(coldStage.getClose());
+ }
+
+ private Group buildGroup() {
+ return Group.newBuilder().setMetadata(Metadata.newBuilder().setName("sw_metric"))
+ .setCatalog(Catalog.CATALOG_MEASURE)
+ .setResourceOpts(ResourceOpts.newBuilder()
+ .setShardNum(2)
+ .setSegmentInterval(
+ IntervalRule.newBuilder()
+ .setUnit(
+ IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 1))
+ .setTtl(
+ IntervalRule.newBuilder()
+ .setUnit(
+ IntervalRule.Unit.UNIT_DAY)
+ .setNum(
+ 7)))
+ .build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITIndexRuleBindingMetadataRegistryTest.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITIndexRuleBindingMetadataRegistryTest.java
new file mode 100644
index 0000000000..cddd767ada
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITIndexRuleBindingMetadataRegistryTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.io.IOException;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.IndexRuleBinding;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.IndexRuleBindingMetadataRegistry;
+import org.apache.skywalking.library.banyandb.v1.client.util.TimeUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.skywalking.library.banyandb.v1.client.BanyanDBClient.DEFAULT_EXPIRE_AT;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ITIndexRuleBindingMetadataRegistryTest extends BanyanDBClientTestCI {
+ private IndexRuleBindingMetadataRegistry registry;
+
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ super.setUpConnection();
+ BanyandbCommon.Group expectedGroup = buildStreamGroup();
+ client.define(expectedGroup);
+ assertNotNull(expectedGroup);
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void testIndexRuleBindingRegistry_createAndGet() throws BanyanDBException {
+ IndexRuleBinding indexRuleBinding = buildIndexRuleBinding();
+ this.client.define(indexRuleBinding);
+ IndexRuleBinding getIndexRuleBinding = this.client.findIndexRuleBinding("sw_record", "trace_binding");
+ assertNotNull(getIndexRuleBinding);
+ getIndexRuleBinding = getIndexRuleBinding.toBuilder()
+ .clearUpdatedAt()
+ .setMetadata(getIndexRuleBinding.getMetadata()
+ .toBuilder()
+ .clearModRevision()
+ .clearCreateRevision())
+ .build();
+ assertEquals(indexRuleBinding, getIndexRuleBinding);
+ assertNotNull(getIndexRuleBinding.getUpdatedAt());
+ }
+
+ @Test
+ public void testIndexRuleBindingRegistry_createAndList() throws BanyanDBException {
+ IndexRuleBinding indexRuleBinding = buildIndexRuleBinding();
+ this.client.define(indexRuleBinding);
+ List<IndexRuleBinding> listIndexRuleBinding = this.client.findIndexRuleBindings("sw_record");
+ assertNotNull(listIndexRuleBinding);
+ IndexRuleBinding actualIndexRuleBinding = listIndexRuleBinding.get(0);
+ actualIndexRuleBinding = actualIndexRuleBinding.toBuilder()
+ .clearUpdatedAt()
+ .setMetadata(actualIndexRuleBinding.getMetadata()
+ .toBuilder()
+ .clearModRevision()
+ .clearCreateRevision())
+ .build();
+ assertEquals(1, listIndexRuleBinding.size());
+ assertEquals(actualIndexRuleBinding, indexRuleBinding);
+ }
+
+ @Test
+ public void testIndexRuleBindingRegistry_createAndDelete() throws BanyanDBException {
+ IndexRuleBinding indexRuleBinding = buildIndexRuleBinding();
+ this.client.define(indexRuleBinding);
+ boolean deleted = this.client.deleteIndexRuleBinding("sw_record", "trace_binding");
+ assertTrue(deleted);
+ assertNull(client.findIndexRuleBinding(indexRuleBinding.getMetadata().getGroup(),
+ indexRuleBinding.getMetadata().getName()
+ ));
+ }
+
+ private IndexRuleBinding buildIndexRuleBinding() {
+ IndexRuleBinding.Builder builder = IndexRuleBinding.newBuilder()
+ .setMetadata(BanyandbCommon.Metadata.newBuilder()
+ .setGroup("sw_record")
+ .setName("trace_binding"))
+ .setSubject(BanyandbDatabase.Subject.newBuilder()
+ .setCatalog(
+ BanyandbCommon.Catalog.CATALOG_STREAM)
+ .setName("trace"))
+ .addAllRules(
+ Arrays.asList("trace_id"))
+ .setBeginAt(TimeUtils.buildTimestamp(ZonedDateTime.of(2024, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)))
+ .setExpireAt(TimeUtils.buildTimestamp(DEFAULT_EXPIRE_AT));
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITIndexRuleMetadataRegistryTest.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITIndexRuleMetadataRegistryTest.java
new file mode 100644
index 0000000000..53ff7dd9cf
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITIndexRuleMetadataRegistryTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Metadata;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.IndexRule;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.IndexRuleMetadataRegistry;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ITIndexRuleMetadataRegistryTest extends BanyanDBClientTestCI {
+ private IndexRuleMetadataRegistry registry;
+
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ super.setUpConnection();
+ BanyandbCommon.Group expectedGroup = buildStreamGroup();
+ client.define(expectedGroup);
+ assertNotNull(expectedGroup);
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void testIndexRuleRegistry_createAndGet() throws BanyanDBException {
+ IndexRule indexRule = buildIndexRule();
+ this.client.define(indexRule);
+ IndexRule getIndexRule = this.client.findIndexRule("sw_record", "trace_id");
+ assertNotNull(getIndexRule);
+ getIndexRule = getIndexRule.toBuilder()
+ .clearUpdatedAt()
+ .setMetadata(getIndexRule.getMetadata()
+ .toBuilder()
+ .clearCreateRevision()
+ .clearModRevision()
+ .clearId())
+ .build();
+ assertEquals(indexRule, getIndexRule);
+ assertNotNull(getIndexRule.getUpdatedAt());
+ }
+
+ @Test
+ public void testIndexRuleRegistry_createAndList() throws BanyanDBException {
+ IndexRule indexRule = buildIndexRule();
+ this.client.define(indexRule);
+ List<IndexRule> listIndexRule = this.client.findIndexRules("sw_record");
+ assertNotNull(listIndexRule);
+ assertEquals(1, listIndexRule.size());
+ IndexRule actualIndexRule = listIndexRule.get(0);
+ actualIndexRule = actualIndexRule.toBuilder()
+ .clearUpdatedAt()
+ .setMetadata(actualIndexRule.getMetadata()
+ .toBuilder()
+ .clearCreateRevision()
+ .clearModRevision()
+ .clearId())
+ .build();
+ assertEquals(actualIndexRule, indexRule);
+ }
+
+ @Test
+ public void testIndexRuleRegistry_createAndUpdate() throws BanyanDBException {
+ this.client.define(buildIndexRule());
+ IndexRule before = client.findIndexRule("sw_record", "trace_id");
+ assertEquals("simple", before.getAnalyzer());
+ IndexRule updatedIndexRule = before.toBuilder().setAnalyzer("standard").build();
+ this.client.update(updatedIndexRule);
+ IndexRule after = this.client.findIndexRule("sw_record", "trace_id");
+ assertNotNull(after);
+ assertEquals("standard", after.getAnalyzer());
+ }
+
+ @Test
+ public void testIndexRuleRegistry_createAndDelete() throws BanyanDBException {
+ IndexRule indexRule = buildIndexRule();
+ client.define(indexRule);
+ boolean deleted = this.client.deleteIndexRule("sw_record", "trace_id");
+ assertTrue(deleted);
+ assertNull(client.findIndexRule(indexRule.getMetadata().getGroup(), indexRule.getMetadata().getName()));
+ }
+
+ private IndexRule buildIndexRule() {
+ IndexRule.Builder builder = IndexRule.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup("sw_record")
+ .setName("trace_id"))
+ .addTags("trace_id")
+ .setType(IndexRule.Type.TYPE_INVERTED)
+ .setAnalyzer("simple");
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITMeasureMetadataRegistryTest.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITMeasureMetadataRegistryTest.java
new file mode 100644
index 0000000000..3c376e75ee
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITMeasureMetadataRegistryTest.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Metadata;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.CompressionMethod;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.EncodingMethod;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Entity;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.FieldSpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.FieldType;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Measure;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.ShardingKey;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagFamilySpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagSpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagType;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.metadata.Duration;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class ITMeasureMetadataRegistryTest extends BanyanDBClientTestCI {
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ super.setUpConnection();
+ BanyandbCommon.Group expectedGroup = buildMeasureGroup();
+ client.define(expectedGroup);
+ assertNotNull(expectedGroup);
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void testMeasureRegistry_createAndGet() throws BanyanDBException {
+ Measure expectedMeasure = buildMeasure();
+ this.client.define(expectedMeasure);
+ Measure actualMeasure = client.findMeasure("sw_metric", "service_cpm_minute");
+ assertNotNull(actualMeasure);
+ assertNotNull(actualMeasure.getUpdatedAt());
+ actualMeasure = actualMeasure.toBuilder().clearUpdatedAt().setMetadata(actualMeasure.getMetadata().toBuilder().clearModRevision().clearCreateRevision()).build();
+ assertEquals(expectedMeasure, actualMeasure);
+ }
+
+ @Test
+ public void testMeasureRegistry_createAndList() throws BanyanDBException {
+ Measure expectedMeasure = buildMeasure();
+ this.client.define(expectedMeasure);
+ List<Measure> actualMeasures = client.findMeasures("sw_metric");
+ assertNotNull(actualMeasures);
+ // _topn_result is a system measure, so there should be 2 measures
+ assertTrue(actualMeasures.size() > 0);
+ boolean found = false;
+ for (Measure actualMeasure : actualMeasures) {
+ if (actualMeasure.getMetadata().getName().equals("service_cpm_minute")) {
+ found = true;
+ actualMeasure = actualMeasure.toBuilder().clearUpdatedAt().setMetadata(actualMeasure.getMetadata().toBuilder().clearModRevision().clearCreateRevision()).build();
+ assertEquals(expectedMeasure, actualMeasure);
+ }
+ }
+ if (!found) {
+ fail("service_cpm_minute not found");
+ }
+ }
+
+ @Test
+ public void testMeasureRegistry_createAndDelete() throws BanyanDBException {
+ Measure expectedMeasure = buildMeasure();
+ this.client.define(expectedMeasure);
+ boolean deleted = this.client.deleteMeasure(
+ expectedMeasure.getMetadata().getGroup(), expectedMeasure.getMetadata().getName());
+ assertTrue(deleted);
+ assertNull(
+ client.findMeasure(expectedMeasure.getMetadata().getGroup(), expectedMeasure.getMetadata().getName()));
+ }
+
+ private Measure buildMeasure() {
+ Measure.Builder builder = Measure.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup("sw_metric")
+ .setName("service_cpm_minute"))
+ .setInterval(Duration.ofMinutes(1).format())
+ .setEntity(Entity.newBuilder().addTagNames("entity_id"))
+ .setShardingKey(ShardingKey.newBuilder().addTagNames("service_id"))
+ .addTagFamilies(
+ TagFamilySpec.newBuilder()
+ .setName("default")
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("entity_id")
+ .setType(
+ TagType.TAG_TYPE_STRING))
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("scope")
+ .setType(
+ TagType.TAG_TYPE_STRING)))
+ .addFields(
+ FieldSpec.newBuilder()
+ .setName("total")
+ .setFieldType(
+ FieldType.FIELD_TYPE_INT)
+ .setCompressionMethod(
+ CompressionMethod.COMPRESSION_METHOD_ZSTD)
+ .setEncodingMethod(
+ EncodingMethod.ENCODING_METHOD_GORILLA))
+ .addFields(
+ FieldSpec.newBuilder()
+ .setName("value")
+ .setFieldType(
+ FieldType.FIELD_TYPE_INT)
+ .setCompressionMethod(
+ CompressionMethod.COMPRESSION_METHOD_ZSTD)
+ .setEncodingMethod(
+ EncodingMethod.ENCODING_METHOD_GORILLA));
+ return builder.build();
+ }
+
+ @Test
+ public void testIndexMeasureRegistry_createAndGet() throws BanyanDBException {
+ Measure expectedMeasure = buildIndexMeasure();
+ this.client.define(expectedMeasure);
+ Measure actualMeasure = client.findMeasure("sw_metric", "service_traffic");
+ assertNotNull(actualMeasure);
+ assertNotNull(actualMeasure.getUpdatedAt());
+ actualMeasure = actualMeasure.toBuilder().clearUpdatedAt().setMetadata(actualMeasure.getMetadata().toBuilder().clearModRevision().clearCreateRevision()).build();
+ assertEquals(expectedMeasure, actualMeasure);
+ }
+
+ private Measure buildIndexMeasure() {
+ Measure.Builder builder = Measure.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup("sw_metric")
+ .setName("service_traffic"))
+ .setEntity(Entity.newBuilder().addTagNames("id"))
+ .setIndexMode(true)
+ .addTagFamilies(
+ TagFamilySpec.newBuilder()
+ .setName("default")
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("id")
+ .setType(
+ TagType.TAG_TYPE_STRING))
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("service_name")
+ .setType(
+ TagType.TAG_TYPE_STRING)));
+ return builder.build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITPropertyMetadataRegistryTest.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITPropertyMetadataRegistryTest.java
new file mode 100644
index 0000000000..6a6526b74f
--- /dev/null
+++ b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITPropertyMetadataRegistryTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.library.banyandb.v1.client;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon.Metadata;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagSpec;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.TagType;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.library.banyandb.v1.client.grpc.exception.NotFoundException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ITPropertyMetadataRegistryTest extends BanyanDBClientTestCI {
+ @BeforeEach
+ public void setUp() throws IOException, BanyanDBException, InterruptedException {
+ super.setUpConnection();
+ BanyandbCommon.Group expectedGroup = buildPropertyGroup();
+ client.define(expectedGroup);
+ assertNotNull(expectedGroup);
+ }
+
+ @AfterEach
+ public void tearDown() throws IOException {
+ this.closeClient();
+ }
+
+ @Test
+ public void testPropertyRegistry_createAndGet() throws BanyanDBException {
+ Property expectedProperty = buildProperty();
+ this.client.define(expectedProperty);
+ Property actualProperty = client.findPropertyDefinition("sw_config", "ui_template");
+ assertNotNull(actualProperty);
+ assertNotNull(actualProperty.getUpdatedAt());
+ actualProperty = actualProperty.toBuilder().clearUpdatedAt().setMetadata(actualProperty.getMetadata().toBuilder().clearModRevision().clearCreateRevision()).build();
+ assertEquals(expectedProperty, actualProperty);
+ }
+
+ @Test
+ public void testPropertyRegistry_createAndList() throws BanyanDBException {
+ Property expectedProperty = buildProperty();
+ this.client.define(expectedProperty);
+ List<Property> actualProperties = client.findPropertiesDefinition("sw_config");
+ assertNotNull(actualProperties);
+ assertEquals(1, actualProperties.size());
+ Property actualProperty = actualProperties.get(0);
+ actualProperty = actualProperty.toBuilder().clearUpdatedAt().setMetadata(actualProperty.getMetadata().toBuilder().clearModRevision().clearCreateRevision()).build();
+ assertEquals(expectedProperty, actualProperty);
+ }
+
+ @Test
+ public void testPropertyRegistry_createAndDelete() throws BanyanDBException {
+ Property expectedProperty = buildProperty();
+ this.client.define(expectedProperty);
+ boolean deleted = this.client.deletePropertyDefinition(
+ expectedProperty.getMetadata().getGroup(), expectedProperty.getMetadata().getName());
+ assertTrue(deleted);
+ assertThrows(
+ NotFoundException.class,
+ () -> client.findPropertyDefinition(
+ expectedProperty.getMetadata().getGroup(), expectedProperty.getMetadata().getName())
+ );
+ }
+
+ private Property buildProperty() {
+ Property.Builder builder = Property.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setGroup("sw_config")
+ .setName("ui_template"))
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("type")
+ .setType(
+ TagType.TAG_TYPE_STRING))
+ .addTags(
+ TagSpec.newBuilder()
+ .setName("service")
+ .setType(
+ TagType.TAG_TYPE_STRING));
+ return builder.build();
+ }
+
+ private BanyandbCommon.Group buildPropertyGroup() {
+ return BanyandbCommon.Group.newBuilder()
+ .setMetadata(Metadata.newBuilder()
+ .setName("sw_config")
+ .build())
+ .setCatalog(BanyandbCommon.Catalog.CATALOG_PROPERTY)
+ .setResourceOpts(BanyandbCommon.ResourceOpts.newBuilder()
+ .setShardNum(2))
+ .build();
+ }
+}
diff --git a/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITStreamMetadataRegistryTest.java b/oap-server/server-library/library-banyandb-client/src/test/java/org/apache/skywalking/library/banyandb/v1/client/ITStreamMetadataRegistryTest.java
new file mode 100644
... 2866 lines suppressed ...