Skip to content

Commit fdc9dc8

Browse files
authored
Merge pull request #613 from Aiven-Open/jeqo/docs-rst
feat: add metrics document generation
2 parents 666924d + 280ed90 commit fdc9dc8

File tree

7 files changed

+605
-25
lines changed

7 files changed

+605
-25
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ all: clean build test
2424

2525
clean:
2626
./gradlew clean
27+
rm -f docs/*.rst
2728

2829
checkstyle:
2930
./gradlew checkstyleMain checkstyleTest checkstyleIntegrationTest
@@ -43,9 +44,14 @@ storage/azure/build/distributions/azure-$(VERSION).tgz:
4344
./gradlew build :storage:azure:distTar -x test -x integrationTest -x e2e:test
4445

4546
.PHONY: docs
46-
docs:
47+
docs: config.rst metrics.rst
48+
49+
config.rst:
4750
./gradlew :docs:genConfigDocs
4851

52+
metrics.rst:
53+
./gradlew :docs:genMetricsDocs
54+
4955
test: build
5056
./gradlew test -x e2e:test
5157

docs/build.gradle

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ dependencies {
2828

2929
tasks.register('genConfigDocs', JavaExec) {
3030
classpath = sourceSets.main.runtimeClasspath
31-
mainClass = 'io.aiven.kafka.tieredstorage.misc.ConfigDocs'
32-
standardOutput = new File("config.rst").newOutputStream()
31+
mainClass = 'io.aiven.kafka.tieredstorage.misc.ConfigsDocs'
32+
standardOutput = new File("docs/configs.rst").newOutputStream()
3333
}
34+
35+
tasks.register('genMetricsDocs', JavaExec) {
36+
classpath = sourceSets.main.runtimeClasspath
37+
mainClass = 'io.aiven.kafka.tieredstorage.misc.MetricsDocs'
38+
standardOutput = new File("docs/metrics.rst").newOutputStream()
39+
}

config.rst renamed to docs/configs.rst

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
=================
2-
RemoteStorageManagerConfig
2+
Core components
33
=================
4+
-----------------
5+
RemoteStorageManagerConfig
6+
-----------------
47
``chunk.size``
58
Segment files are chunked into smaller parts to allow for faster processing (e.g. encryption, compression) and for range-fetching. It is recommended to benchmark this value, starting with 4MiB.
69

@@ -91,9 +94,9 @@ RemoteStorageManagerConfig
9194
* Importance: low
9295

9396

94-
=================
97+
-----------------
9598
SegmentManifestCacheConfig
96-
=================
99+
-----------------
97100
Under ``fetch.manifest.cache.``
98101

99102
``retention.ms``
@@ -129,9 +132,9 @@ Under ``fetch.manifest.cache.``
129132
* Importance: low
130133

131134

132-
=================
135+
-----------------
133136
SegmentIndexesCacheConfig
134-
=================
137+
-----------------
135138
Under ``fetch.indexes.cache.``
136139

137140
``retention.ms``
@@ -167,9 +170,9 @@ Under ``fetch.indexes.cache.``
167170
* Importance: low
168171

169172

170-
=================
173+
-----------------
171174
ChunkManagerFactoryConfig
172-
=================
175+
-----------------
173176
``fetch.chunk.cache.class``
174177
Chunk cache implementation. There are 2 implementations included: io.aiven.kafka.tieredstorage.fetch.cache.MemoryChunkCache and io.aiven.kafka.tieredstorage.fetch.cache.DiskChunkCache
175178

@@ -179,9 +182,9 @@ ChunkManagerFactoryConfig
179182
* Importance: medium
180183

181184

182-
=================
185+
-----------------
183186
MemoryChunkCacheConfig
184-
=================
187+
-----------------
185188
Under ``fetch.chunk.cache.``
186189

187190
``size``
@@ -224,9 +227,9 @@ Under ``fetch.chunk.cache.``
224227
* Importance: low
225228

226229

227-
=================
230+
-----------------
228231
DiskChunkCacheConfig
229-
=================
232+
-----------------
230233
Under ``fetch.chunk.cache.``
231234

232235
``path``
@@ -452,15 +455,15 @@ S3StorageConfig
452455
* Importance: low
453456

454457
``s3.api.call.attempt.timeout``
455-
AWS S3 API call attempt timeout in milliseconds
458+
AWS S3 API call attempt (single retry) timeout in milliseconds
456459

457460
* Type: long
458461
* Default: null
459462
* Valid Values: null or [1,...,9223372036854775807]
460463
* Importance: low
461464

462465
``s3.api.call.timeout``
463-
AWS S3 API call timeout in milliseconds
466+
AWS S3 API call timeout in milliseconds, including all retries
464467

465468
* Type: long
466469
* Default: null

docs/metrics.rst

Lines changed: 367 additions & 0 deletions
Large diffs are not rendered by default.

docs/src/main/java/io/aiven/kafka/tieredstorage/misc/ConfigDocs.java renamed to docs/src/main/java/io/aiven/kafka/tieredstorage/misc/ConfigsDocs.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,34 @@
3737
/**
3838
* Gather all config definitions across the project and generate a documentation page
3939
**/
40-
public class ConfigDocs {
40+
public class ConfigsDocs {
4141
public static void main(final String[] args) {
42-
printSectionTitle("RemoteStorageManagerConfig");
42+
printSectionTitle("Core components");
43+
44+
printSubsectionTitle("RemoteStorageManagerConfig");
4345
final var rsmConfigDef = RemoteStorageManagerConfig.configDef();
4446
System.out.println(rsmConfigDef.toEnrichedRst());
4547

46-
printSectionTitle("SegmentManifestCacheConfig");
48+
printSubsectionTitle("SegmentManifestCacheConfig");
4749
System.out.println("Under ``" + SEGMENT_MANIFEST_CACHE_PREFIX + "``\n");
4850
final var segmentManifestCacheDef = MemorySegmentManifestCache.configDef();
4951
System.out.println(segmentManifestCacheDef.toEnrichedRst());
5052

51-
printSectionTitle("SegmentIndexesCacheConfig");
53+
printSubsectionTitle("SegmentIndexesCacheConfig");
5254
System.out.println("Under ``" + FETCH_INDEXES_CACHE_PREFIX + "``\n");
5355
final var segmentIndexesCacheDef = MemorySegmentIndexesCache.configDef();
5456
System.out.println(segmentIndexesCacheDef.toEnrichedRst());
5557

56-
printSectionTitle("ChunkManagerFactoryConfig");
58+
printSubsectionTitle("ChunkManagerFactoryConfig");
5759
final var chunkCacheFactoryDef = ChunkManagerFactoryConfig.configDef();
5860
System.out.println(chunkCacheFactoryDef.toEnrichedRst());
5961

60-
printSectionTitle("MemoryChunkCacheConfig");
62+
printSubsectionTitle("MemoryChunkCacheConfig");
6163
System.out.println("Under ``" + FETCH_CHUNK_CACHE_PREFIX + "``\n");
6264
final var memChunkCacheDef = ChunkCacheConfig.configDef(new ConfigDef());
6365
System.out.println(memChunkCacheDef.toEnrichedRst());
6466

65-
printSectionTitle("DiskChunkCacheConfig");
67+
printSubsectionTitle("DiskChunkCacheConfig");
6668
System.out.println("Under ``" + FETCH_CHUNK_CACHE_PREFIX + "``\n");
6769
final var diskChunkCacheDef = DiskChunkCacheConfig.configDef();
6870
System.out.println(diskChunkCacheDef.toEnrichedRst());
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* Copyright 2024 Aiven Oy
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.aiven.kafka.tieredstorage.misc;
18+
19+
import java.util.LinkedHashMap;
20+
import java.util.Map;
21+
import java.util.TreeMap;
22+
23+
import org.apache.kafka.common.MetricName;
24+
import org.apache.kafka.common.MetricNameTemplate;
25+
import org.apache.kafka.common.metrics.Metrics;
26+
import org.apache.kafka.common.utils.Sanitizer;
27+
28+
import io.aiven.kafka.tieredstorage.fetch.cache.ChunkCache;
29+
import io.aiven.kafka.tieredstorage.fetch.index.MemorySegmentIndexesCache;
30+
import io.aiven.kafka.tieredstorage.fetch.manifest.MemorySegmentManifestCache;
31+
import io.aiven.kafka.tieredstorage.metrics.CaffeineMetricsRegistry;
32+
import io.aiven.kafka.tieredstorage.metrics.MetricsRegistry;
33+
import io.aiven.kafka.tieredstorage.metrics.ThreadPoolMonitorMetricsRegistry;
34+
35+
public class MetricsDocs {
36+
public static void main(final String[] args) {
37+
printSectionTitle("Core components metrics");
38+
System.out.println();
39+
printSubsectionTitle("RemoteStorageManager metrics");
40+
System.out.println();
41+
System.out.println(toRstTable(MetricsRegistry.METRIC_CONTEXT, new MetricsRegistry().all()));
42+
43+
System.out.println();
44+
printSubsectionTitle("SegmentManifestCache metrics");
45+
System.out.println();
46+
System.out.println(toRstTable(
47+
CaffeineMetricsRegistry.METRIC_CONTEXT,
48+
new CaffeineMetricsRegistry(MemorySegmentManifestCache.METRIC_GROUP).all()));
49+
System.out.println();
50+
System.out.println(toRstTable(
51+
ThreadPoolMonitorMetricsRegistry.METRIC_CONFIG,
52+
new ThreadPoolMonitorMetricsRegistry(MemorySegmentManifestCache.THREAD_POOL_METRIC_GROUP).all()));
53+
54+
System.out.println();
55+
printSubsectionTitle("SegmentIndexesCache metrics");
56+
System.out.println(toRstTable(
57+
CaffeineMetricsRegistry.METRIC_CONTEXT,
58+
new CaffeineMetricsRegistry(MemorySegmentIndexesCache.METRIC_GROUP).all()));
59+
System.out.println(toRstTable(
60+
ThreadPoolMonitorMetricsRegistry.METRIC_CONFIG,
61+
new ThreadPoolMonitorMetricsRegistry(MemorySegmentIndexesCache.THREAD_POOL_METRIC_GROUP).all()));
62+
System.out.println();
63+
printSubsectionTitle("ChunkCache metrics");
64+
System.out.println();
65+
System.out.println(toRstTable(
66+
CaffeineMetricsRegistry.METRIC_CONTEXT,
67+
new CaffeineMetricsRegistry(ChunkCache.METRIC_GROUP).all()));
68+
System.out.println();
69+
System.out.println(toRstTable(
70+
ThreadPoolMonitorMetricsRegistry.METRIC_CONFIG,
71+
new ThreadPoolMonitorMetricsRegistry(ChunkCache.THREAD_POOL_METRIC_GROUP).all()));
72+
73+
System.out.println();
74+
printSectionTitle("Storage Backend metrics");
75+
System.out.println();
76+
printSubsectionTitle("AzureBlobStorage metrics");
77+
System.out.println();
78+
System.out.println(toRstTable(
79+
io.aiven.kafka.tieredstorage.storage.azure.MetricRegistry.METRIC_CONTEXT,
80+
new io.aiven.kafka.tieredstorage.storage.azure.MetricRegistry().all()));
81+
System.out.println();
82+
printSubsectionTitle("GcsStorage metrics");
83+
System.out.println();
84+
System.out.println(toRstTable(
85+
io.aiven.kafka.tieredstorage.storage.gcs.MetricRegistry.METRIC_CONTEXT,
86+
new io.aiven.kafka.tieredstorage.storage.gcs.MetricRegistry().all()));
87+
System.out.println();
88+
printSubsectionTitle("S3Storage metrics");
89+
System.out.println();
90+
System.out.println(toRstTable(
91+
io.aiven.kafka.tieredstorage.storage.s3.MetricRegistry.METRIC_CONTEXT,
92+
new io.aiven.kafka.tieredstorage.storage.s3.MetricRegistry().all()));
93+
}
94+
95+
// o.a.k.common.metrics.Metrics does only have generation of Html documentation.
96+
// as there is no plans to publish HTML docs, this util method is added to generate RST.
97+
// may be upstreamed.
98+
static String toRstTable(final String domain, final Iterable<MetricNameTemplate> allMetrics) {
99+
final Map<String, Map<String, String>> beansAndAttributes = new TreeMap<>();
100+
101+
try (final Metrics metrics = new Metrics()) {
102+
for (final MetricNameTemplate template : allMetrics) {
103+
final Map<String, String> tags = new LinkedHashMap<>();
104+
for (final String s : template.tags()) {
105+
tags.put(s, "{" + s + "}");
106+
}
107+
108+
final MetricName metricName = metrics.metricName(
109+
template.name(),
110+
template.group(),
111+
template.description(),
112+
tags
113+
);
114+
final String beanName = getMBeanName(domain, metricName);
115+
beansAndAttributes.computeIfAbsent(beanName, k -> new TreeMap<>());
116+
final Map<String, String> attrAndDesc = beansAndAttributes.get(beanName);
117+
if (!attrAndDesc.containsKey(template.name())) {
118+
attrAndDesc.put(template.name(), template.description());
119+
} else {
120+
throw new IllegalArgumentException(
121+
"mBean '" + beanName
122+
+ "' attribute '"
123+
+ template.name()
124+
+ "' is defined twice."
125+
);
126+
}
127+
}
128+
}
129+
130+
final StringBuilder b = new StringBuilder();
131+
132+
for (final Map.Entry<String, Map<String, String>> e : beansAndAttributes.entrySet()) {
133+
// Add mBean name as a section title
134+
b.append(e.getKey()).append("\n");
135+
b.append("=".repeat(e.getKey().length())).append("\n\n");
136+
137+
// Determine the maximum lengths for each column
138+
final int maxAttrLength = Math.max("Attribute name".length(),
139+
e.getValue().keySet().stream().mapToInt(String::length).max().orElse(0));
140+
final int maxDescLength = Math.max("Description".length(),
141+
e.getValue().values().stream().mapToInt(String::length).max().orElse(0));
142+
143+
// Create the table header
144+
final String headerFormat = "%-" + maxAttrLength + "s %-" + maxDescLength + "s\n";
145+
final String separatorLine = "=" + "=".repeat(maxAttrLength) + " " + "=".repeat(maxDescLength) + "\n";
146+
147+
b.append(separatorLine);
148+
b.append(String.format(headerFormat, "Attribute name", "Description"));
149+
b.append(separatorLine);
150+
151+
// Add table rows
152+
for (final Map.Entry<String, String> e2 : e.getValue().entrySet()) {
153+
b.append(String.format(headerFormat, e2.getKey(), e2.getValue()));
154+
}
155+
156+
// Close the table
157+
b.append(separatorLine);
158+
b.append("\n"); // Add an empty line between tables
159+
}
160+
161+
return b.toString();
162+
}
163+
164+
// same as o.a.k.common.metrics.JmxReporter#getMBeanName but copy/pasted
165+
// to avoid adding another dependency to this module.
166+
static String getMBeanName(final String prefix, final MetricName metricName) {
167+
final StringBuilder beanName = new StringBuilder();
168+
beanName.append(prefix);
169+
beanName.append(":type=");
170+
beanName.append(metricName.group());
171+
for (final Map.Entry<String, String> entry : metricName.tags().entrySet()) {
172+
if (entry.getKey().length() <= 0 || entry.getValue().length() <= 0) {
173+
continue;
174+
}
175+
beanName.append(",");
176+
beanName.append(entry.getKey());
177+
beanName.append("=");
178+
beanName.append(Sanitizer.jmxSanitize(entry.getValue()));
179+
}
180+
return beanName.toString();
181+
}
182+
183+
static void printSectionTitle(final String title) {
184+
System.out.println("=================\n"
185+
+ title + "\n"
186+
+ "=================");
187+
}
188+
189+
static void printSubsectionTitle(final String title) {
190+
System.out.println("-----------------\n"
191+
+ title + "\n"
192+
+ "-----------------");
193+
}
194+
}

storage/s3/src/main/java/io/aiven/kafka/tieredstorage/storage/s3/S3StorageConfig.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,11 @@ public class S3StorageConfig extends AbstractConfig {
6363
static final int S3_MULTIPART_UPLOAD_PART_SIZE_DEFAULT = S3_MULTIPART_UPLOAD_PART_SIZE_MIN;
6464

6565
private static final String S3_API_CALL_TIMEOUT_CONFIG = "s3.api.call.timeout";
66-
private static final String S3_API_CALL_TIMEOUT_DOC = "AWS S3 API call timeout in milliseconds";
66+
private static final String S3_API_CALL_TIMEOUT_DOC = "AWS S3 API call timeout in milliseconds, "
67+
+ "including all retries";
6768
private static final String S3_API_CALL_ATTEMPT_TIMEOUT_CONFIG = "s3.api.call.attempt.timeout";
68-
private static final String S3_API_CALL_ATTEMPT_TIMEOUT_DOC = "AWS S3 API call attempt timeout in milliseconds";
69+
private static final String S3_API_CALL_ATTEMPT_TIMEOUT_DOC = "AWS S3 API call attempt "
70+
+ "(single retry) timeout in milliseconds";
6971
public static final String AWS_CREDENTIALS_PROVIDER_CLASS_CONFIG = "aws.credentials.provider.class";
7072
private static final String AWS_CREDENTIALS_PROVIDER_CLASS_DOC = "AWS credentials provider. "
7173
+ "If not set, AWS SDK uses the default "

0 commit comments

Comments
 (0)