Skip to content

Commit 02eaddc

Browse files
authored
Bump up API to support lifecycle and property (#78)
1 parent 73553cb commit 02eaddc

File tree

14 files changed

+642
-94
lines changed

14 files changed

+642
-94
lines changed
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip
22
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar
3-

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Release Notes.
1111
* Bump up the API to support the new property.
1212
* Bump up the API to adopt the status field which is changed to the string type due to the compatibility issue.
1313
* Bump up the API to support getting the API version.
14+
* Bump up the API to support the lifecycle management.
1415

1516
0.7.0
1617
------------------

README.md

Lines changed: 93 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,62 @@ client.define(g);
7171

7272
Then we may define a stream with customized configurations.
7373

74+
#### Define a how-warm-cold Group
75+
76+
Here illustrates how to use the lifecycle stages feature for hot-warm-cold data architecture:
77+
78+
```java
79+
// build a group sw_record for Stream with hot-warm-cold lifecycle stages
80+
Group g = Group.newBuilder().setMetadata(Metadata.newBuilder().setName("sw_record"))
81+
.setCatalog(Catalog.CATALOG_STREAM)
82+
.setResourceOpts(ResourceOpts.newBuilder()
83+
// Hot configuration
84+
.setShardNum(3)
85+
// Default segment interval (will be overridden by stages if defined)
86+
.setSegmentInterval(
87+
IntervalRule.newBuilder()
88+
.setUnit(IntervalRule.Unit.UNIT_DAY)
89+
.setNum(1))
90+
// Default TTL (will be overridden by stages if defined)
91+
.setTtl(
92+
IntervalRule.newBuilder()
93+
.setUnit(IntervalRule.Unit.UNIT_DAY)
94+
.setNum(3))
95+
// Define lifecycle stages (hot → warm → cold)
96+
.addStages(LifecycleStage.newBuilder()
97+
.setName("warm")
98+
.setShardNum(2) // Fewer shards
99+
.setSegmentInterval(IntervalRule.newBuilder()
100+
.setUnit(IntervalRule.Unit.UNIT_DAY)
101+
.setNum(1))
102+
.setTtl(IntervalRule.newBuilder()
103+
.setUnit(IntervalRule.Unit.UNIT_DAY)
104+
.setNum(7)) // Keep in warm for 7 days
105+
.setNodeSelector("hdd-nodes") // Store on cheaper HDD nodes
106+
.build())
107+
.addStages(LifecycleStage.newBuilder()
108+
.setName("cold")
109+
.setShardNum(1) // Minimal shards for archived data
110+
.setSegmentInterval(IntervalRule.newBuilder()
111+
.setUnit(IntervalRule.Unit.UNIT_DAY)
112+
.setNum(7)) // Larger segments for cold data
113+
.setTtl(IntervalRule.newBuilder()
114+
.setUnit(IntervalRule.Unit.UNIT_DAY)
115+
.setNum(30)) // Keep in cold for 30 more days
116+
.setNodeSelector("archive-nodes") // Store on archive nodes
117+
.setClose(true) // Close segments that are no longer live
118+
.build()))
119+
.build();
120+
client.define(g);
121+
```
122+
123+
This configuration creates a hot-warm-cold architecture where:
124+
- Hot stage: Data is stored on fast SSD nodes with many shards for 1 day, optimized for high query performance
125+
- Warm stage: Data moves to HDD nodes with fewer shards for 7 days, balanced between performance and cost
126+
- Cold stage: Data finally moves to archive nodes with minimal shards for 30 days, optimized for storage efficiency
127+
128+
Data automatically flows through these stages according to the defined TTLs. The total retention of data is 38 days (1+7+30).
129+
74130
#### Define a Stream
75131
```java
76132
// build a stream trace with above group
@@ -469,68 +525,64 @@ MeasureWrite measureWrite = client.createMeasureWrite("sw_metric", "service_cpm_
469525
CompletableFuture<Void> f = measureBulkWriteProcessor.add(measureWrite);
470526
f.get(10, TimeUnit.SECONDS);
471527
```
528+
# Property APIs
472529

473-
## Property APIs
474-
475-
Property APIs are used to store key-value pairs.
476-
477-
### Apply(Create/Update)
478-
479-
`apply` will always succeed whenever the property exists or not.
480-
The old value will be overwritten if already existed, otherwise a new value will be set.
530+
Before using properties, you need to define a property schema:
481531

482532
```java
483-
Property property = Property.create("default", "sw", "ui_template")
484-
.addTag(TagAndValue.newStringTag("name", "hello"))
485-
.addTag(TagAndValue.newStringTag("state", "successd"))
486-
.build();
487-
this.client.apply(property); //created:true tagsNum:2
533+
// Define property schema
534+
BanyandbDatabase.Property propertyDef =
535+
BanyandbDatabase.Property.newBuilder()
536+
.setMetadata(Metadata.newBuilder()
537+
.setGroup("default")
538+
.setName("ui_template"))
539+
.setTagType(TagType.TAG_TYPE_STRING)
540+
.build();
541+
542+
client.define(propertyDef);
488543
```
489544

490-
The operation supports updating partial tags.
545+
After defining the schema, you can apply (create/update) properties:
491546

492547
```java
493-
Property property = Property.create("default", "sw", "ui_template")
494-
.addTag(TagAndValue.newStringTag("state", "failed"))
548+
// Apply a property (create or update)
549+
Property property = Property.newBuilder()
550+
.setMetadata(Metadata.newBuilder()
551+
.setGroup("default")
552+
.setName("ui_template"))
553+
.setId("dashboard-1")
554+
.setTagValue(BanyandbModel.TagValue.newBuilder()
555+
.setStr("template-data-json"))
495556
.build();
496-
this.client.apply(property); //created:false tagsNum:1
497-
```
498-
499-
### Query
500557

501-
Property can be queried via `Client.findProperty`,
502-
503-
```java
504-
BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
505-
.addGroups("default")
506-
.setContainer("sw")
507-
.addIds("ui_template")
508-
.build());
558+
ApplyResponse response = client.apply(property);
509559
```
510560

511-
The query operation could filter tags,
561+
You can also apply with a specific strategy:
512562

513563
```java
514-
BanyandbProperty.QueryResponse resp = client.query(BanyandbProperty.QueryRequest.newBuilder()
515-
.addGroups("default")
516-
.setContainer("sw")
517-
.addIds("ui_template")
518-
.addTagProjection("state")
519-
.build());
564+
// Apply with merge strategy
565+
ApplyResponse response = client.apply(property, Strategy.STRATEGY_MERGE);
520566
```
521567

522-
### Delete
523-
524-
Property can be deleted by calling `Client.deleteProperty`,
568+
Query properties:
525569

526570
```java
527-
this.client.deleteProperty("default", "sw", "ui_template"); //deleted:true tagsNum:2
571+
// Query properties
572+
BanyandbProperty.QueryRequest queryRequest = BanyandbProperty.QueryRequest.newBuilder()
573+
.setMetadata(Metadata.newBuilder()
574+
.setGroup("default")
575+
.setName("ui_template"))
576+
.build();
577+
578+
BanyandbProperty.QueryResponse queryResponse = client.query(queryRequest);
528579
```
529580

530-
The delete operation could remove specific tags instead of the whole property.
581+
Delete a property:
531582

532583
```java
533-
this.client.deleteProperty("default", "sw", "ui_template", "state"); //deleted:true tagsNum:1
584+
// Delete a property
585+
DeleteResponse deleteResponse = client.deleteProperty("default", "ui_template", "dashboard-1");
534586
```
535587

536588
# Compiling project

src/main/java/org/apache/skywalking/banyandb/v1/client/BanyanDBClient.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import org.apache.skywalking.banyandb.v1.client.metadata.IndexRuleMetadataRegistry;
6363
import org.apache.skywalking.banyandb.v1.client.metadata.MeasureMetadataRegistry;
6464
import org.apache.skywalking.banyandb.v1.client.metadata.MetadataCache;
65+
import org.apache.skywalking.banyandb.v1.client.metadata.PropertyMetadataRegistry;
6566
import org.apache.skywalking.banyandb.v1.client.metadata.ResourceExist;
6667
import org.apache.skywalking.banyandb.v1.client.metadata.StreamMetadataRegistry;
6768
import org.apache.skywalking.banyandb.v1.client.metadata.TopNAggregationMetadataRegistry;
@@ -812,6 +813,64 @@ public List<IndexRuleBinding> findIndexRuleBindings(String group) throws BanyanD
812813
return registry.list(group);
813814
}
814815

816+
/**
817+
* Define a new property.
818+
*
819+
* @param property the property to be stored in the BanyanBD
820+
* @throws BanyanDBException if the property is invalid
821+
*/
822+
public void define(org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property property) throws BanyanDBException {
823+
PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
824+
registry.create(property);
825+
}
826+
827+
/**
828+
* Update the property.
829+
*
830+
* @param property the property to be stored in the BanyanBD
831+
* @throws BanyanDBException if the property is invalid
832+
*/
833+
public void update(org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property property) throws BanyanDBException {
834+
PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
835+
registry.update(property);
836+
}
837+
838+
/**
839+
* Find the property with given group and name
840+
*
841+
* @param group group of the metadata
842+
* @param name name of the metadata
843+
* @return the property found in BanyanDB. Otherwise, null is returned.
844+
*/
845+
public org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property findPropertyDefinition(String group, String name) throws BanyanDBException {
846+
PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
847+
return registry.get(group, name);
848+
}
849+
850+
/**
851+
* Find the properties with given group
852+
*
853+
* @param group group of the metadata
854+
* @return the properties found in BanyanDB
855+
*/
856+
public List<org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property> findPropertiesDefinition(String group) throws BanyanDBException {
857+
PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
858+
return registry.list(group);
859+
}
860+
861+
/**
862+
* Delete the property
863+
*
864+
* @param group group of the metadata
865+
* @param name name of the metadata
866+
* @param id identity of the property
867+
* @return if this property has been deleted
868+
*/
869+
public boolean deletePropertyDefinition(String group, String name) throws BanyanDBException {
870+
PropertyMetadataRegistry registry = new PropertyMetadataRegistry(checkNotNull(this.channel));
871+
return registry.delete(group, name);
872+
}
873+
815874
/**
816875
* Apply(Create or update) the property with {@link BanyandbProperty.ApplyRequest.Strategy#STRATEGY_MERGE}
817876
*
@@ -1046,6 +1105,20 @@ public ResourceExist existTopNAggregation(String group, String name) throws Bany
10461105
return new TopNAggregationMetadataRegistry(checkNotNull(this.channel)).exist(group, name);
10471106
}
10481107

1108+
/**
1109+
* Check if the given property exists.
1110+
*
1111+
* @param group group of the property
1112+
* @param name name of the property
1113+
* @return ResourceExist which indicates whether group and property exist
1114+
*/
1115+
public ResourceExist existProperty(String group, String name) throws BanyanDBException {
1116+
Preconditions.checkArgument(!Strings.isNullOrEmpty(group));
1117+
Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
1118+
1119+
return new PropertyMetadataRegistry(checkNotNull(this.channel)).exist(group, name);
1120+
}
1121+
10491122
/**
10501123
* Update the stream metadata cache from the server
10511124
* @param group the group of the stream

src/main/java/org/apache/skywalking/banyandb/v1/client/PropertyStore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public DeleteResponse delete(String group, String name, String id) throws Banyan
5454
return HandleExceptionsWith.callAndTranslateApiException(() ->
5555
this.stub.delete(BanyandbProperty.DeleteRequest.newBuilder()
5656
.setGroup(group)
57-
.setContainer(name)
57+
.setName(name)
5858
.setId(id)
5959
.build()));
6060
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.banyandb.v1.client.metadata;
20+
21+
import io.grpc.Channel;
22+
import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
23+
import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
24+
import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase.Property;
25+
import org.apache.skywalking.banyandb.database.v1.PropertyRegistryServiceGrpc;
26+
import org.apache.skywalking.banyandb.v1.client.grpc.MetadataClient;
27+
import org.apache.skywalking.banyandb.v1.client.grpc.exception.BanyanDBException;
28+
29+
import java.util.List;
30+
31+
public class PropertyMetadataRegistry extends MetadataClient<PropertyRegistryServiceGrpc.PropertyRegistryServiceBlockingStub,
32+
BanyandbDatabase.Property> {
33+
34+
public PropertyMetadataRegistry(Channel channel) {
35+
super(PropertyRegistryServiceGrpc.newBlockingStub(channel));
36+
}
37+
38+
@Override
39+
public long create(final Property payload) throws BanyanDBException {
40+
BanyandbDatabase.PropertyRegistryServiceCreateResponse resp = execute(() ->
41+
stub.create(BanyandbDatabase.PropertyRegistryServiceCreateRequest.newBuilder()
42+
.setProperty(payload)
43+
.build()));
44+
return resp.getModRevision();
45+
}
46+
47+
@Override
48+
public void update(final Property payload) throws BanyanDBException {
49+
execute(() ->
50+
stub.update(BanyandbDatabase.PropertyRegistryServiceUpdateRequest.newBuilder()
51+
.setProperty(payload)
52+
.build()));
53+
}
54+
55+
@Override
56+
public boolean delete(final String group, final String name) throws BanyanDBException {
57+
BanyandbDatabase.PropertyRegistryServiceDeleteResponse resp = execute(() ->
58+
stub.delete(BanyandbDatabase.PropertyRegistryServiceDeleteRequest.newBuilder()
59+
.setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
60+
.build()));
61+
return resp != null && resp.getDeleted();
62+
}
63+
64+
@Override
65+
public Property get(final String group, final String name) throws BanyanDBException {
66+
BanyandbDatabase.PropertyRegistryServiceGetResponse resp = execute(() ->
67+
stub.get(BanyandbDatabase.PropertyRegistryServiceGetRequest.newBuilder()
68+
.setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
69+
.build()));
70+
71+
return resp.getProperty();
72+
}
73+
74+
@Override
75+
public ResourceExist exist(String group, String name) throws BanyanDBException {
76+
BanyandbDatabase.PropertyRegistryServiceExistResponse resp = execute(() ->
77+
stub.exist(BanyandbDatabase.PropertyRegistryServiceExistRequest.newBuilder()
78+
.setMetadata(BanyandbCommon.Metadata.newBuilder().setGroup(group).setName(name).build())
79+
.build()));
80+
return ResourceExist.create(resp.getHasGroup(), resp.getHasProperty());
81+
}
82+
83+
@Override
84+
public List<Property> list(final String group) throws BanyanDBException {
85+
BanyandbDatabase.PropertyRegistryServiceListResponse resp = execute(() ->
86+
stub.list(BanyandbDatabase.PropertyRegistryServiceListRequest.newBuilder()
87+
.setGroup(group)
88+
.build()));
89+
90+
return resp.getPropertiesList();
91+
}
92+
}

0 commit comments

Comments
 (0)