Compare commits

...

460 Commits

Author SHA1 Message Date
keqing 252b108227
fix(core): split e2e 2025-11-27 14:55:32 +08:00
Xu Han@AutoMQ fd103496d3
feat(s3stream): S3 API timeout can be configured via the path parameter (#3062)
Co-authored-by: yx9o <yangx_soft@163.com>
2025-11-27 10:26:52 +08:00
Xu Han@AutoMQ 02a9f55072
revert(s3wal): handle hollow after trim (#3059)
Revert "fix(s3wal): handle hollow after trim (#3057)"

This reverts commit 478b49400b.
2025-11-26 21:07:05 +08:00
Xu Han@AutoMQ 478b49400b
fix(s3wal): handle hollow after trim (#3057)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-26 20:59:51 +08:00
Xu Han@AutoMQ c3591979cd
fix(metastream): compaction may drop certain keys (#3055)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-26 19:36:49 +08:00
woshigaopp 3d6b4c42ff
fix(core): fix e2e (#3050)
fix enterprise remote write e2e
2025-11-26 16:47:04 +08:00
Xu Han@AutoMQ 47094288f4
revert(wal): revert wal change in #3049 (#3051)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-26 16:43:08 +08:00
Xu Han@AutoMQ f08f224995
fix(failover): refresh failover task when the epoch bump (#3048)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-26 15:37:36 +08:00
Xu Han@AutoMQ b6cbdb7b00
feat(failover): don't failover the recovering node (#3044)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-25 22:45:17 +08:00
woshigaopp 0e843b8c93
fix(e2e): fix e2e 1.6 (#3042)
fix e2e
2025-11-25 21:12:26 +08:00
1sonofqiu 945cfb383c
fix(metrics): inject offset metric register for txn commit (#3038) (#3039) 2025-11-24 19:42:56 +08:00
Xu Han@AutoMQ 9bdf046914
feat(s3stream): auto adjust walUploadThreshold (#3035)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-24 10:51:48 +08:00
Gezi-lzq 32c8b9e7b6
fix(RecordAssembler): optimize field creation using Accessor (#3031) (#3032) 2025-11-20 14:43:33 +08:00
Xu Han@AutoMQ ad5fb80ad4
fix(benchmark): fix compression so it works even when not batching (#3030)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-19 20:01:40 +08:00
Xu Han@AutoMQ db5f5ce875
fix(network): fix potential ByteBuf LEAK in fetch (#3028)
* fix(network): fix potential ByteBuf LEAK in fetch

Signed-off-by: Robin Han <hanxvdovehx@gmail.com>

* feat(network): cherry-pick #2337

Signed-off-by: Robin Han <hanxvdovehx@gmail.com>

---------

Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-19 19:38:40 +08:00
Gezi-lzq b44f39cb46
chore(release): release 1.6.1-rc0 (#3026)
feat(release): release 1.6.1-rc0
2025-11-19 14:23:35 +08:00
Gezi-lzq 4974ced37c
feat(protobuf): enhance Protobuf data handling with LogicalMap support and enhance test coverage (#3020) (#3025) 2025-11-19 11:44:29 +08:00
Gezi-lzq 711cee7043
fix(schema): streamline schema change handling and enhance test coverage (#3019) (#3024) 2025-11-19 11:44:19 +08:00
Xu Han@AutoMQ 451566e890
fix(logcache): guard cache free with writeLock (#3022)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-19 11:24:33 +08:00
Gezi-lzq 42c42c24e6
fix(binder): enhance RecordBinder and TypeAdapter to support STRUCT type conversion (#3005) (#3023)
* fix(binder): enhance RecordBinder and TypeAdapter to support STRUCT type conversion

* test(binder): add tests for nested struct binding and schema instance reuse

* fix(adapter): support conversion from Number to TIMESTAMPTZ in AbstractTypeAdapter

* fix(adapter): update list and map conversion methods to include StructConverter
2025-11-19 11:19:32 +08:00
Xu Han@AutoMQ fb9fcdde33
perf(s3stream): async heavy log cache operation (#3017)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-17 19:13:55 +08:00
woshigaopp f8170951b8
fix(core): auto add ops metrcis (#3013) 2025-11-15 19:03:05 +08:00
Xu Han@AutoMQ 17d3e5f6ab
chore(metrics): move queue time record to AsyncNetworkBandwidthLimiter (#3011)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-14 17:30:15 +08:00
woshigaopp dd708215ff
feat(connect): support connect openTelemetry and log for 1.6 (#2961)
Extract metrics and logging features as standalone modules.
2025-11-14 17:19:19 +08:00
Xu Han@AutoMQ 7e67790649
fix(wal): fix leak caused by #3000 (#3008)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-14 10:54:52 +08:00
Xu Han@AutoMQ 0ae65d872d
feat(zerozone2): add overload protection (#3002) (#3003)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-13 11:58:54 +08:00
Xu Han@AutoMQ 1ef8405237
fix(wal): filter inner trim record for range get (#3001)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-11 19:19:36 +08:00
Xu Han@AutoMQ b88e07422d
perf(s3stream): optimize limiter lock range (#2998) (#2999)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-11 17:30:32 +08:00
Xu Han@AutoMQ ea4d785f49
perf(s3stream): optimize logcache merge (#2997)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-10 22:02:09 +08:00
Xu Han@AutoMQ f28e242143
perf(s3stream): parallel append & optimize logcache init (#2995)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-10 20:17:51 +08:00
Xu Han@AutoMQ 3acc1f83eb
perf(zerozone2): smooth wal upload (#2993)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-10 16:32:55 +08:00
Gezi-lzq d62b97492a
feat(timestamp): enhance RecordBinder to support TIMESTAMP and TIME types in union handling (#2981) (#2988)
* feat(timestamp): enhance RecordBinder to support TIMESTAMP and TIME types in union handling

* test(avro): add missing import for AvroRecordBinderTest
2025-11-04 14:53:56 +08:00
Gezi-lzq ee31e100b7
perf(s3stream): compute compaction delay using min timestamp instead of sorting (#2984) (#2989)
Co-authored-by: JasirVoriya <jasirvoriya@gmail.com>
2025-11-04 14:53:46 +08:00
Xu Han@AutoMQ 33aa6e5416
fix(zerozone2): fix the upgrade from v1 to v2 (#2985)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-04 02:23:29 +08:00
Xu Han@AutoMQ 152d74bd11
feat(logcache): limit max block count (#2982) (#2983)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-11-03 12:30:41 +08:00
Gezi-lzq 55c409e9be
feat(release): release 1.6.0 (#2977) 2025-10-28 15:27:47 +08:00
Gezi-lzq 7c47716be0
fix: correctly use bytebuffer slice in decodecontexts method (#2925) (#2976)
* fix: correctly use bytebuffer slice in decodecontexts method



* eckstyle] [ERROR] RESOLVED

---------

Signed-off-by: Tino Britty  <153193545+brittytino@users.noreply.github.com>
Co-authored-by: Tino Britty <153193545+brittytino@users.noreply.github.com>
2025-10-28 14:28:04 +08:00
Xu Han@AutoMQ 1299fb82c5
chore(zerozone2): detect close failure (#2974)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-24 01:45:06 +08:00
Gezi-lzq 40972e2cc0
feat(table-coordinator): handle exceptions during snapshot expiration (#2969) (#2970) 2025-10-21 20:28:58 +08:00
Gezi-lzq 7d05bf7c00
feat(core): add iceberg-nessie dependency for enhanced functionality (#2967) (#2968) 2025-10-21 12:08:44 +08:00
Xu Han@AutoMQ f7d223cc76
feat(zerozone2): fast acks=0 (#2964)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-17 15:22:52 +08:00
Xu Han@AutoMQ 0e8b088691
feat(zerozone2): async start RouterChannel and ConfirmWAL (#2960)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-16 20:49:45 +08:00
Xu Han@AutoMQ b474bbf8d2
chore(zerozone2): schedule logging proxy mapping (#2958)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-16 18:52:28 +08:00
Xu Han@AutoMQ 2d88b922f7
feat(failover): remove the controller limitation (#2956)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-16 16:08:06 +08:00
Xu Han@AutoMQ 7e97c2740f
feat(zerozone2): response retryable error when route out get UNKNOWN_SERVER_ERROR (#2954)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-16 11:10:26 +08:00
Xu Han@AutoMQ d33e3dbbc0
fix(zerzone2): fix commited epoch not bump when there is gracefully shutdown node (#2952)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-15 15:58:57 +08:00
Xu Han@AutoMQ 3877544e9c
perf(s3stream): optimize the doRangeRead GC issue caused by #2764 (#2949)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-14 19:58:32 +08:00
Xu Han@AutoMQ e06a137605
fix(zerozone2): fail the recover when decode fail (#2947)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-14 17:32:54 +08:00
Xu Han@AutoMQ 3c6ed43b5d
chore(zerozone2): isolate the record encode allocator (#2945)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-13 20:10:57 +08:00
Xu Han@AutoMQ bb2218cf26
fix(zerozone2): fix duplicated release (#2943)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-13 12:53:28 +08:00
Gezi-lzq 1ab2ecfb5f
feat(release): release 1.6.0-rc2 (#2941) 2025-10-13 11:21:17 +08:00
Xu Han@AutoMQ c604211cde
fix(zerozone2): release after append & better snapshot replay (#2939)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-12 22:59:19 +08:00
Xu Han@AutoMQ b9ff2e4f97
fix(zerozone2): fix bug caused by #2929 (#2937)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-11 14:09:33 +08:00
Gezi-lzq 3ce475fd7f
feat(security): enhance ClientUtils to include listener-specific security settings (#2933) (#2935)
* feat(security): enhance ClientUtils to include listener-specific security settings

* chore: spotless apply
2025-10-11 11:05:34 +08:00
Xu Han@AutoMQ b14df25205
feat(zerozone2): return NOT_LEADER_OR_FOLLOWER to stale produce (#2932)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-10 18:53:28 +08:00
Robin Han 82e86ab49f fix(zerozone2): reset replayer when reset subscriber
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-10 17:47:41 +08:00
yx9o 520e952e03 fix(s3stream): correct mismatch between configuration class defaults and profile property defaults (#2924) 2025-10-09 15:12:41 +08:00
Nick Guo 4659e20239 refactor(tool): adjust consume logic in `automq-perf-test.sh` tool (#2900) 2025-10-09 15:12:41 +08:00
Xu Han@AutoMQ 273753d9ec
fix(zerozone2): support lazy stream (#2923)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-10-02 21:02:52 +08:00
Xu Han@AutoMQ e9fd57f205
fix(zerozone2): fix the snapshot to avoid consuming abort txn record (#2921)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-30 18:23:35 +08:00
Gezi-lzq 2ad29cb5ef
feat(release): release 1.6.0-rc1 (#2919) 2025-09-29 18:48:23 +08:00
Gezi-lzq 69cbbe4ce0
fix: ensure S3 endpoint is not required for table topic since AWS client can guess it most of the time (#2915) (#2917)
Co-authored-by: Romain Manni-Bucau <rmannibucau@gmail.com>
2025-09-29 17:52:23 +08:00
Gezi-lzq 3d2dae191f
feat(avro): simplify union handling in AvroValueAdapter and add support for nested union types (#2916) (#2918) 2025-09-29 17:49:02 +08:00
Xu Han@AutoMQ 26faa487be
fix(log): elastic log supports truncate (#2914)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-29 14:26:54 +08:00
Xu Han@AutoMQ 1dc38d289f
fix(zerozone2): fix epoch missing update when there isn't new record (#2912)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-29 11:53:15 +08:00
Xu Han@AutoMQ 7a18d9f393
chore(zerozone2): clean up s3stream metrics (#2910)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-28 17:26:54 +08:00
Xu Han@AutoMQ dc032bb446
fix(zerozone2): fix txn record overwrite by mistake (#2906)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-28 16:01:27 +08:00
Xu Han@AutoMQ 91cc7b37c2
fix(zerozone2): guard snapshot by lock (#2908)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-28 15:55:12 +08:00
Gezi-lzq bf4eca7bc3
feat(benchmark): enhance BenchmarkResult to track duration in nanoseconds and add field count metrics (#2898) (#2901)
* feat(benchmark): enhance BenchmarkResult to track duration in nanoseconds and add field count metrics

* feat(adapter): improve map conversion handling and enhance field counting logic

* feat(transform): refactor enriched schema cache to use SchemaKey for improved uniqueness handling

* feat(metric): reduce granularity for binary buffers to optimize performance

* feat(metric): reduce granularity for binary buffers to optimize performance

* feat(binder): optimize map field count calculation in RecordBinder

* feat(transform): simplify fingerprint computation in DebeziumUnwrapTransform
2025-09-28 14:54:12 +08:00
Xu Han@AutoMQ af3cf27f84
fix(issue2902): change Verification.waitingRequests to unbounded queue to fix the deadlock (#2904)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-27 18:11:16 +08:00
Xu Han@AutoMQ fcde520734
fix(s3stream): fix potential merge read fail (#2897)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-26 15:26:02 +08:00
Gezi-lzq 2751e22e5d
feat(tests): add Protobuf ConverterTest for comprehensive Protobuf conversion validation (#2885) (#2891)
* feat(tests): add Protobuf ConverterTest for comprehensive Protobuf conversion validation

* fix(tests): reorder imports in ProtobufRegistryConverterTest for clarity
2025-09-25 20:37:05 +08:00
Xu Han@AutoMQ 063a183f65
fix(zerozone2): gracefully await subscriber close (#2895)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-25 20:35:55 +08:00
Xu Han@AutoMQ 0c435339ad
fix(wal): try drain all waiting upload bulks (#2889)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-24 19:16:54 +08:00
Xu Han@AutoMQ 1b04239fb8
fix(wal): limit bulk upload delay (#2887)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-24 18:24:03 +08:00
Gezi-lzq fb8aef6677
feat(metadata): add retain and release methods for improved image management (#2882) (#2884) 2025-09-24 15:41:25 +08:00
Gezi-lzq 583d6a0f98
feat(metrics): integrate new metrics for table delay, fields per second, and event loop busy ratio (#2876) (#2881)
* feat(metrics): integrate new metrics for table topic delay and fields per second

* feat(metrics): add event loop busy ratio metric for table worker

* feat(network): enhance network permit handling and improve blocking consumption
2025-09-23 14:10:19 +08:00
Gezi-lzq 82b5fc78da
fix(partition): handle null partition spec in commit response (#2877) (#2880) 2025-09-23 10:22:57 +08:00
Gezi-lzq ed292fb31e
feat(perf): add Avro and Protobuf performance test cases and related configurations (#2873) (#2879) 2025-09-23 10:22:47 +08:00
Gezi-lzq 6a9b36be2d
feat(converter): support union types for list and map conversions in AvroValueAdapter (#2872) (#2878)
* feat(converter): support union types for list and map conversions in AvroValueAdapter

* fix(factory): update value converter creation to use schema ID in RecordProcessorFactory
2025-09-23 10:22:34 +08:00
Xu Han@AutoMQ cb677cd154
feat(wal): simple direct channel wrapper (#2874)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-22 14:43:32 +08:00
Xu Han@AutoMQ 6666170d11
chore(s3stream): remove useless wal code (#2871)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-19 18:41:43 +08:00
Xu Han@AutoMQ 48ab2cf7c7
fix(metrics): set the interval for the lazy-created histogram (#2869)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-19 14:21:03 +08:00
Xu Han@AutoMQ 6892e90474
fix(zerozone2): close snapshotReadPartitionsManager (#2867)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-19 09:58:19 +08:00
Gezi-lzq b4f62449c8
feat: enhance schema handling and optimize record processing (#2855) (#2862)
* fix(converter): improve schema identity generation and optimize RecordAssembler

* feat: enhance schema handling and optimize record processing

* feat: improve schema field handling and normalize record values

* feat: enhance schema generation and improve handling of optional fields

* fix: reorder import statements for better organization
2025-09-17 19:30:26 +08:00
Xu Han@AutoMQ cdb485b8c9
fix(zerozone2): fix the expired offsetForLeaderEpoch (#2864)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-17 19:05:19 +08:00
allen 941f5c444e
feat(metrics): enhance metrics for benchmark (#2860) (#2861) 2025-09-17 17:47:17 +08:00
Xu Han@AutoMQ a19960bb20
fix(zerozone2): await subscriber close (#2859)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-17 15:58:46 +08:00
allen 2b313b37ea
feat(metrics): enhance metrics for benchmark (#2856) (#2857)
feat(metrics): enhance metrics for benchmark (#2856) (#2857)
2025-09-17 12:03:27 +08:00
Xu Han@AutoMQ 60d8e59d1e
fix(zerozone2): fix reset bug cause skip (#2854)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-16 21:58:57 +08:00
allen bb635078d9
fix(metrics): fix percentile metrics (#2849) (#2852) 2025-09-16 18:16:05 +08:00
Xu Han@AutoMQ bd32b6b765
fix(zerozone2): adapt to txn (#2851)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-16 17:40:39 +08:00
Xu Han@AutoMQ 2a250d644a
fix(log): fix potential fetch NPE when the partition is closing (#2847)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-16 15:07:02 +08:00
Gezi-lzq 91a9168e99
feat: add tabletopic e2e case (#2827) (#2840)
* feat(perf): implement random Avro value generation for performance testing

* feat(e2e): implement end-to-end tests for Table Topic feature with Avro messages

* feat(tests): add end-to-end tests for Table Topic feature with schema evolution and broker restart scenarios

* fix(docker): streamline Docker installation in Dockerfile and update run command in ducker-ak

* fix(config): update S3 bucket name for Iceberg catalog in configuration
2025-09-15 22:03:54 +08:00
Xu Han@AutoMQ e10409f1f9
fix(s3stream): fix buf leak when recover fail (#2842)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-15 21:08:58 +08:00
Gezi-lzq 7baebfa4d5
fix(config): remove exception for invalid list string in TableTopicConfigValidator (#2836) (#2839) 2025-09-15 15:00:34 +08:00
Xu Han@AutoMQ 4f6519d3db
fix(zerozone2): partition open conflict (#2837)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-15 14:08:29 +08:00
Gezi-lzq 5adaf353be
fix(config): add support for latest schema subject and message name configuration in TopicConfig and WorkerConfig (#2834) (#2835) 2025-09-15 11:10:41 +08:00
Gezi-lzq 6bca87ea9c
fix(converter): update subject and message name retrieval in ConverterFactory and WorkerConfig (#2831) (#2832)
fix(config): correct spelling of 'subject' in WorkerConfig
2025-09-11 21:17:59 +08:00
1sonofqiu 76235d76e8
feat(docker): kafka docker image adapt (#2830)
* feat(docker): kafka docker image adapt (#2828)
2025-09-10 18:12:12 +08:00
Nick Guo 078b8c3d7d
feat(strimzi): add docker build workflow for strimzi (#2826)
feat(strimzi): add docker build workflow for strimzi (#2802)
2025-09-09 11:40:47 +08:00
allen 4285feffc4
feat(nfs-wal): initial implementation of nfs wal (#2824) 2025-09-08 17:44:59 +08:00
Robin Han d2d5ee9422 feat(release): release 1.6.0-rc0
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-08 15:57:51 +08:00
Robin Han 4aff134466 feat(reassign): optimize partition reassignment to avoid client fast retry
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-08 15:43:14 +08:00
Gezi-lzq 6f4056bb17
chore(config): update default commit interval to 1 minute for table topic (#2819) (#2820) 2025-09-08 11:39:08 +08:00
Gezi-lzq a9d81cba61 feat(table): integrate RecordProcessor architecture and enhance error tolerance (#2814)
* feat(table): integrate RecordProcessor architecture and enhance error tolerance

* feat(process): handle system-level errors in schema registry interactions

* chore(process): fix code style

* feat(tests): update assertions for schema evolution and error handling in RecordProcessor tests

* feat(process): simplify error handling for RestClientException in DefaultRecordProcessor

* feat(errors): enhance ErrorsTolerance enum and streamline error tolerance configuration

* feat(metrics): improve field count calculations for List and Map types

* chore(format): improve code style in RecordBinder.java

* feat(writer): modify offset range computation
2025-09-05 21:11:11 +08:00
Gezi-lzq 99180f052e feat(process): introduce record processor factory and enrich conversion/transform pipeline (#2796)
* feat(process): introduce flexible record processor factory and enrich conversion/transform pipeline

- Add RecordProcessorFactory to support dynamic creation of processors based on schema and transform configs
- Refactor RegistryConverterFactory for improved schema format handling and converter instantiation
- Implement SchemaFormat, TableTopicConvertType, and TableTopicTransformType enums for config-driven processing
- Enhance Converter interface and conversion records to include key, value, and timestamp fields
- Refactor AvroRegistryConverter and ProtobufRegistryConverter to return unified RecordData objects
- Add ProtoToAvroConverter for robust Protobuf-to-Avro conversion
- Update transform chain: add KafkaMetadataTransform for metadata enrichment, refactor DebeziumUnwrapTransform
- Update DefaultRecordProcessor and TransformContext to support partition-aware processing
- Improve error handling and code clarity across conversion and transform modules
2025-09-05 21:11:11 +08:00
Gezi-lzq cbaf0741b9 feat(binder): add batch field count statistics to RecordBinder and AvroRecordView (#2795) 2025-09-05 21:11:11 +08:00
Gezi-lzq 1780adae21 refactor(process): Improve and clarify data processing error handling (#2792)
refactor(process): unify error handling with InvalidDataException and update DataError types
2025-09-05 21:11:11 +08:00
Gezi-lzq 1f1caccdac feat(process): introduce unified record processing pipeline with converter and transform interfaces (#2786) 2025-09-05 21:11:11 +08:00
Gezi-lzq 0f7b3583cb feat(binder): implement AvroValueAdapter and RecordBinder for Avro to Iceberg conversion (#2744)
* feat(binder): implement AvroValueAdapter and RecordBinder for Avro to Iceberg conversion

* chore(binder): preallocate list and map sizes
2025-09-05 21:11:11 +08:00
Xu Han@AutoMQ 18a39c1938
fix(workflow): set the gradle version (#2815)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-05 17:44:37 +08:00
Xu Han@AutoMQ 659a31d957
chore(workflows): add publish maven package workflow (#2813)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-05 15:33:33 +08:00
Xu Han@AutoMQ 5492fb38f3
perf(zerozone2): optimize the wal batch upload strategy (#2810)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-05 10:51:49 +08:00
Xu Han@AutoMQ 62e8e456b0
fix(zerozone2): fix potential WAL reset overflow BUG (#2806)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-03 16:04:15 +08:00
Xu Han@AutoMQ aa67bb78fa
feat(java): set min java version to 17 (#2804)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-03 10:49:48 +08:00
Xu Han@AutoMQ 5109c90a8a
chore(e2e): accelerate e2e (#2800)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-09-01 20:10:17 +08:00
Xu Han@AutoMQ 4139ea4b29
chore(zerozone2): rename channelOffset targetNodeId to attributes (#2794)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-29 17:09:13 +08:00
Xu Han@AutoMQ 0ec42d6333
feat(zerozone2): force route to local (#2790)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-28 21:45:24 +08:00
Xu Han@AutoMQ b534857b3a
refactor(zerozone2): make wal provider more extensible (#2787) (#2788)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-27 16:57:05 +08:00
Xu Han@AutoMQ e63042f3e2
chore(metrics): unify object wal metrics (#2785)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-26 20:28:13 +08:00
Xu Han@AutoMQ 55dbf25bc0
chore(config): dynamic change config based on heap size (#2783) 2025-08-26 20:24:43 +08:00
Xu Han@AutoMQ 9313a68534
fix(zerozone2): fix failover bug caused by #2764 (#2781)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-25 13:03:26 +08:00
yx9o 37010faf98 fix(core): fix lock object error when implementing singleton pattern 2025-08-25 10:28:47 +08:00
Gezi-lzq 775a817b33
fix(metrics): optimize metrics name mapping in S3MetricsExporter (#2775) (#2776) 2025-08-22 15:06:23 +08:00
Xu Han@AutoMQ 6d92c4ecd3 feat(zerozone2): complete delay fetch (#2773)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-21 16:11:21 +08:00
Yu Ning 4e7486095e fix(tools/perf): revert partial changes in #2514 to fix bug (#2609)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-08-21 16:11:21 +08:00
Sankalpa Sarkar 72a9e90286 feat: Added consumer percentage control for catch-up reading in perf test (#2573)
* Add documentation for --consumers-during-catchup parameter

This adds documentation comments for the new parameter that allows
controlling the percentage of consumers activated during catch-up reads.

* Add --consumers-during-catchup parameter to PerfConfig

Introduces a new parameter to control what percentage of consumers
should be activated during catch-up read scenarios. Default is 100%.
Includes validation to ensure the value is between 0 and 100.

* Enhance ConsumerService to support partial consumer activation

Modifies resetOffset() and resume() methods to accept a percentage parameter,
allowing only a subset of consumers to be activated during catch-up reads.
Maintains backwards compatibility with existing method signatures.

* Update PerfCommand to use consumers-during-catchup parameter

Updates the catch-up read logic to use the new parameter for controlling
the percentage of consumers that should be activated during catch-up reads.

* Fix various code style issues for whitespace and line breaks to
  comply with project standards

* fixed the two Checkstyle issues again 😭

* Implements a new --consumers-during-catchup parameter to control what percentage of topics have active consumers during catch-up reading phases. This parameter allows users to simulate realistic scenarios where only a subset of topics experience catch-up reads simultaneously.

* removes unnecessary pause
2025-08-21 16:11:21 +08:00
Aryan Hardik Dani 75253dc0fa feat: Add --catchup-topic-prefix for performance testing and solve the issue (#2514)
feat: Add --catchup-topic-prefix for performance testing
2025-08-21 16:11:21 +08:00
Xu Han@AutoMQ 82a043e7ee feat(zerozone2): add metrics (#2772)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-21 16:11:21 +08:00
Xu Han@AutoMQ bb198a1ed4 fix(zerozone2): fix the replay blocking when ObjectNotExist (#2771)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-21 16:11:21 +08:00
Xu Han@AutoMQ 56b65eadb5 fix(s3stream): move network inbound consumption from s3stream to kafkaapi (#2770)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-21 16:11:21 +08:00
Xu Han@AutoMQ ebcf1a7f7f chore(s3stream): increase part size from 16MB to 32MB (#2769)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-21 16:11:21 +08:00
Xiao Yang 3d9bf9f551 refactor(core): support dynamic splicing factory class name #2757 2025-08-21 16:11:21 +08:00
Xu Han@AutoMQ da28fd0f2a feat(zerozone2): support zero zone v2 (#2766)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-18 17:36:19 +08:00
Xu Han@AutoMQ dfb930bf8c feat(zerozone2): add router channel epoch manager (#2765)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-18 17:36:19 +08:00
Xu Han@AutoMQ ed6625cf8a feat(zerozone2): S3WAL supports sequential append and distributed read (#2764)
- S3WAL supports sequential append & callback
- Simple the S3Storage logic by S3WAl sequential append
- S3WAL supports distributed read from another nodes
- S3Storage supports linked record
2025-08-18 17:36:19 +08:00
Xu Han@AutoMQ cb53dd514a feat(zerozone2): extend ZoneRouter & PartitionSnapshot protocol (#2763)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-18 17:36:19 +08:00
Robin Han 0403aa2cfc
fix(failover): fix failover get wrong node config
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-15 18:27:14 +08:00
woshigaopp f8781a9be4
fix(e2e): cherry pick to fix e2e (#2756)
remove unnecessary imports to fix e2e.
2025-08-14 19:26:17 +08:00
Xu Han@AutoMQ c1e4cb7b96
feat(release): release 1.5.4 (#2755)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-08-14 15:20:43 +08:00
Xu Han@AutoMQ d50499eea6
fix(s3stream): fix the network out over-consumed (#2752) (#2753)
Consider the following scenario:
1. A Fetch request contains partitions P1 and P2. The data of P1 is in LogCache, while the data of P2 is not.
2. First, a fast read will be attempted. At this time, P1 will return data and consume Network Out, and P2 will return a FastReadException.
3. Due to the FastReadException, the entire Fetch attempts a slow read. At this time, both P1 and P2 return data and consume Network Out.
4. At this point, the Network Out in step 2 is consumed repeatedly.

Solution: Move the S3Stream network out consumption to ElasticReplicaManager. Avoid the network out traffic over-consumed, when there are mixin(tail read & catch-up read) partitions reading.
2025-08-13 10:52:40 +08:00
Xu Han@AutoMQ 8641ba864c
fix(s3stream): add pending requests await timeout for S3Stream#close (#2751) 2025-08-12 17:05:36 +08:00
Xu Han@AutoMQ 387557b10d
fix: Added support for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (#2748)
fix: Added support for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY  (#2747)

Added support for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to the
list of supported environment variables. Issue #2746

Co-authored-by: Saumya Pandey <saumyapandeycse98@gmail.com>
2025-08-11 11:30:52 +08:00
Gezi-lzq bd9b78ee46
fix(streamReader): implement scheduled cleanup for expired stream readers (#2719) (#2735)
- Add Time dependency to StreamReader and StreamReaders for time-related operations
- Update constructors to accept Time, defaulting to Time.SYSTEM
- Replace System.currentTimeMillis() with time.milliseconds() throughout
- Refactor StreamReadersTest to use MockTime for simulating time passage
- Remove reflection-based time manipulation in tests for cleaner and safer testing

---------

Signed-off-by: Gezi-lzq <lzqtxwd@gmail.com>
2025-07-31 10:45:18 +08:00
woshigaopp 6462c967a5
feat: modify for enterprise e2e (#2733)
* feat: modify for enterprise e2e

* feat: add AutoMQ inject start/end

* feat: undo modify runclass.sh
2025-07-30 23:52:41 +08:00
Gezi-lzq 219dba3e95
perf(log): increase FETCH_BATCH_SIZE to 512KB in StreamSegmentInputStream (#2722) (#2730) 2025-07-30 22:13:11 +08:00
Gezi-lzq 92b6f7ef44
fix(log): Prevent potential offset overflow in ElasticLogSegment (#2720) (#2726)
* fix(log): Prevent potential offset overflow in ElasticLogSegment

This commit addresses an issue where a log segment could accommodate more than Integer.MAX_VALUE records, leading to a potential integer overflow when calculating relative offsets.

The root cause was that the check `offset - baseOffset <= Integer.MAX_VALUE` allowed a relative offset to be exactly `Integer.MAX_VALUE`. Since offsets are 0-based, this allows for `Integer.MAX_VALUE + 1` records, which cannot be represented by a standard Integer.

This fix implements the following changes:
1.  In `ElasticLogSegment`, the offset validation is changed from `<=` to `< Integer.MAX_VALUE` to ensure the relative offset strictly fits within an Integer's bounds.
2.  In `LogCleaner`, a new segment grouping method `groupSegmentsBySizeV2` is introduced for `ElasticUnifiedLog`. This method uses the same stricter offset check to prevent incorrectly grouping segments that would exceed the offset limit.
3.  The corresponding unit tests in `LogCleanerTest` have been updated to reflect these new boundaries and validate the fix.

Fixes: #2718

* fix(logCleaner): unify segment grouping logic

* fix(logCleaner): extract offset range check for segment grouping to prevent overflow in ElasticLogSegment

* style(logCleaner): fix indentation in segment grouping condition for readability

* style(logCleaner): fix line break in offset range check for readability

* chore: add AutoMQ inject

* style(logCleaner): remove unnecessary blank line after segment grouping

* fix(stream): validate record batch count to prevent negative values in append
2025-07-30 12:42:02 +08:00
Gezi-lzq a885e5ae8c
fix(logCleaner): optimize write buffer management and clear buffer before use (#2704) (#2711) 2025-07-29 18:32:53 +08:00
woshigaopp 93087a07f9
fix: resolve Base64 decoding error in certificate parsing (#2615) (#2… (#2707)
fix: resolve Base64 decoding error in certificate parsing (#2615) (#2693)

- Fix IllegalArgumentException: Illegal base64 character 20 in S3StreamKafkaMetricsManager
- Replace single newline removal with comprehensive whitespace cleanup using replaceAll("\s", "")
- Add graceful error handling for both Base64 and certificate parsing failures
- Add comprehensive unit tests covering various whitespace scenarios and edge cases
- Improve logging with specific error messages for failed certificate parsing

Fixes #2615

(cherry picked from commit 75bdea05e5)

Co-authored-by: Vivek Chavan <111511821+vivekchavan14@users.noreply.github.com>
2025-07-28 17:25:24 +08:00
Gezi-lzq ca0e9bf40f
feat(log): enhance reading logic to handle offset gaps and add unit tests (#2699) (#2701) 2025-07-28 14:09:27 +08:00
Xu Han@AutoMQ 3e0af68ae9
chore(github): change code owners (#2696)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-07-25 10:56:32 +08:00
Xu Han@AutoMQ a9ee4f8e7b
perf(s3stream): avoid S3StreamSetObject objectId long primitive unbox… (#2694)
perf(s3stream): avoid S3StreamSetObject objectId long primitive unboxing (#2687)

Co-authored-by: lifepuzzlefun <wjl_is_213@163.com>
2025-07-25 10:56:04 +08:00
Gezi-lzq 60d671f706
feat(catalog): avoid static global credentials provider (#2684) (#2686)
* feat(catalog): Avoid static global credentials provider

Refactors `CredentialProviderHolder` to prevent "Connection Pool Shutdown"
errors by creating a new provider instance for each catalog.

Previously, a single static `AwsCredentialsProvider` was shared globally.
If this provider was closed, it would affect all subsequent operations.
By creating a new provider on each `create()` call from Iceberg, this
change removes the global singleton and isolates provider instances.

Fixes #2680

* Update core/src/main/java/kafka/automq/table/CredentialProviderHolder.java




* fix(credentials): update DefaultCredentialsProvider instantiation to use builder pattern

---------

Signed-off-by: Gezi-lzq <lzqtxwd@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-18 18:03:29 +08:00
Yu Ning 5a8af4b89d
feat(block-wal): use version V1 and support sequential return (#2681)
* feat(s3stream/block-wal): complete appends sequentially (#2665)

* feat(s3stream/block-wal): complete appends sequentially



* fix: use a lock to ensure there is at most one callback thread



---------



* feat(s3stream/wal): write a padding record when no space at the end of device (#2673)

* refactor(RecordHeader): remove useless methods



* feat(SlidingWindowService): write a padding block when not enough space



* feat(recovery): handle padding records



* fix: fix incorrect assertion



---------



* feat(s3stream/wal): defaults to using version V1 and forward compatible (#2676)

* feat: introduce the Block WAL V1



* feat: impl `RecoverIteratorV1` which only recovers continuous records



* feat: wal forward compatibility



* test: fix tests



* test: test recover from WAL V1



* test: test upgrade



---------



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-07-17 14:15:29 +08:00
Gezi-lzq a0500b4562
fix(worker): update CommitResponse to use partition type from writerF… (#2679)
fix(worker): update CommitResponse to use partition type from writerFactory (#2677)

* fix(worker): update CommitResponse to use partition type from writerFactory

* fix(worker): mock partitionSpec in TopicPartitionsWorkerTest for unpartitioned partitions

* fix(worker): reorganize imports in TopicPartitionsWorkerTest for clarity
2025-07-17 11:14:47 +08:00
Xu Han@AutoMQ 38541af171
perf(misc): optimize FairLimiter implementation (#2670) (#2672)
* fix(s3stream): avoid StreamMetadataManager add callback when retry

* perf(misc): optimize FairLimiter implementation

Co-authored-by: lifepuzzlefun <wjl_is_213@163.com>
2025-07-17 10:08:09 +08:00
Gezi-lzq 0cd047c2ce
fix: support list more than 1000 objects by prefix (#2660) (#2666)
This commit fixes an issue where the doList method in AwsObjectStorage.java
did not handle paginated results from the S3 listObjectsV2 API. The
method now recursively fetches all pages of objects, ensuring that it can
retrieve more than the default 1000 objects.
2025-07-14 14:14:08 +08:00
Xu Han@AutoMQ caaa41718d
feat(release): 1.5.3 (#2663)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-07-10 11:17:03 +08:00
1sonofqiu 8d624daf56
chore(container): update welcome message and container patch (#2658) (#2664) 2025-07-10 11:16:52 +08:00
Gezi-lzq 1dfa8f75a0
fix(s3stream): avoid StreamMetadataManager add callback when retry (#… (#2661)
fix(s3stream): avoid StreamMetadataManager add callback when retry (#2659)

Co-authored-by: lifepuzzlefun <wjl_is_213@163.com>
2025-07-08 18:10:56 +08:00
Xu Han@AutoMQ f473d7da1a
fix(image): guard streams image access with lock to prevent data loss (#2653) (#2654)
fix(image): guard streams image access with lock to prevent compaction skip data

Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-07-04 18:39:06 +08:00
Gezi-lzq 73642827c3
feat(release): release 1.5.3-rc0 (#2651) 2025-07-03 18:37:10 +08:00
Gezi-lzq a67349f845
fix(deps): resolve logging conflicts from Hive Metastore (#2648) (#2649)
feat(dependencies): add jcl-over-slf4j library and exclude conflicting logging implementations
2025-07-03 16:48:35 +08:00
Yu Ning 76195e23b9
chore(tools/perf): log client logs to a separate file (#2645)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-07-02 14:49:21 +08:00
Xu Han@AutoMQ a9cf9b23ec
feat(release): release 1.5.2 (#2639)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-06-25 19:34:18 +08:00
Gezi-lzq e86e00ae45
fix(docker): update minio policy commands to use 'anonymous' instead of 'policy' (#2640) (#2641)
fix(docker): update minio policy commands to use 'anonymous' instead of 'public'
2025-06-25 19:09:06 +08:00
Xu Han@AutoMQ 2c42dec469
feat(tabletopic): default include hive catalog dependencies (#2637)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-06-25 16:02:49 +08:00
Gezi-lzq 5e7806178d
fix(metadata): ensure correct GroupCoordinator updates for topic dele… (#2635)
fix(metadata): ensure correct GroupCoordinator updates for topic deletions (#2626)
2025-06-22 10:50:36 +08:00
Shichao Nie fe462777e6
feat(auto_balancer): only use pending append/fetch latency to identif… (#2632)
feat(auto_balancer): only use pending append/fetch latency to identify slow broker

Signed-off-by: Shichao Nie <niesc@automq.com>
2025-06-18 11:47:34 +08:00
Gezi-lzq beb23756d4
fix(docker): update AutoMQ image version to 1.5.1 in docker-compose files (#2630) 2025-06-17 16:00:14 +08:00
Gezi-lzq 92094b533b
fix(docker): update MinIO command from config host to alias set (#2628)
* fix(docker): update MinIO command from config host to alias set

* fix(docker): update MinIO and mc images to specific release versions
2025-06-17 11:57:33 +08:00
Yu Ning b3c1f10813
fix(quota): update broker quota configs on "--broker-defaults" (#2618)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-06-09 18:04:14 +08:00
woshigaopp 47a4517a4d
fix(e2e): cherry pick fix e2e 1.5 (#2616)
* fix(e2e): fix e2e test performance and log compaction

* fix(e2e): fix e2e test reassign and round_trip

* fix(e2e): fix e2e test GroupModeTransactionsTest

* fix(e2e): fix e2e test reassignTest

* fix(e2e): fix e2e test kafka start failed because not support file wal

* fix(e2e): fix e2e test kafka start failed because not support file wal

* fix(e2e): fix e2e test kafka start failed because not support file wal

* fix(e2e): fix e2e test kafka start failed because wait logic

* fix(e2e): fix e2e test kafka start failed because wait too short

* fix(e2e): format code

* fix(e2e): fix e2e test kafka start failed because not support file wal

* fix(e2e): format code

* fix(e2e): format code

* fix(e2e): format code
2025-06-06 18:45:34 +08:00
Yu Ning 1119acc065
feat(tools/perf): log cpu, memory usage and min latency (#2611)
* chore(tools/perf): add an option "--send-throughput"

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat(tools/perf): log cpu and memory usages (#2607)

* feat: introduce `CpuMonitor`

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: introduce `MemoryMonitor`

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat(tools/perf): log cpu and memory usages

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat(tools/perf): log the min latency (#2608)

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-06-03 20:29:37 +08:00
Yu Ning 0fc2c7f0bd
perf(s3client): increase outbound network limiter pool size (#2603)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-05-29 18:01:17 +08:00
Yu Ning c59127cd2a
fix(s3client): limit the network usage of object storages (#2600)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-05-29 16:54:41 +08:00
Yu Ning 6995c3f64d
perf(s3stream): recover and upload data in segments (#2596)
perf(s3stream): recover and upload data in segments (#2593)

* feat: pause recovery once the cache is full



* feat(s3stream): recover and upload data in segments



* test: test segmented recovery



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-05-28 11:05:54 +08:00
Xu Han@AutoMQ 2b407e0786
fix(s3stream): fix node epoch outdated (#2595)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-23 18:22:11 +08:00
Yu Ning 92aef1d11e
refactor(s3stream): preparation for segmented recovery (#2592)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-05-23 16:15:40 +08:00
Yu Ning 8f58a419c9
refactor(s3stream): in preparation for segmented recovery (#2589) (#2590)
* refacotr: remove useless param passing



* revert: "release Bytebuf allocated by WAL earlier to prevent memory fragmentation (#2341)"

This reverts commit 7b4240aa31.

* refactor: extract `filterOutInvalidStreams`



* refactor: extract `releaseRecords`



* refactor: rename "expectXXX" to "expectedXXX"



* refactor: extract methods



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-05-23 11:39:54 +08:00
1sonofqiu 39bc9dae1f
chore(chart): update demo-values.yaml to correct AWS environment variable names (#2588) 2025-05-22 14:14:46 +08:00
Xu Han@AutoMQ f6bf3e64f8
fix(stream): obey aws auth chain (#2585)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-22 11:44:55 +08:00
Xu Han@AutoMQ 8ec58aa0c7
chore(all): make code more extendable (#2582)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-22 10:46:40 +08:00
Yu Ning 86071df7b0
perf(tool/perf): reduce the record header size (#2580)
* perf(tool/perf): reduce the record header size

Signed-off-by: Ning Yu <ningyu@automq.com>

* style: fix lint

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-05-20 10:20:49 +08:00
Xu Han@AutoMQ dbef35334d
feat(release): release 1.5.0 (#2577)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-19 11:59:05 +08:00
Xu Han@AutoMQ 52871652c0
feat(docker): cherry pick #2468 #2469 (#2576)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-19 11:16:54 +08:00
1sonofqiu 4afee951c4
feat(container): cp_2561_2570_2562_2574 (#2575)
* feat(container): automq kafka container features and patch file for u… (#2561)

feat(container): automq kafka container features and patch file for upgrade

* fix(dockerfile): update Dockerfile and scripts for AutoMQ packaging im… (#2570)

fix(container): update Dockerfile and scripts for AutoMQ packaging improvements

* feat(docker): Docker release for bitnami chart (#2563)

* feat(docker): add GitHub Actions workflow for bitnami chart Docker image release

* feat(docker): add GitHub Actions workflow for bitnami chart Docker image release

* fix(docker): update image tag format for automq-bitnami in release workflow and temporarily remove the latest tag until the AutoMQ Docker Compose is refactored into Bitnami Docker Compose.

* feat(helm): add demo-values.yaml and update README for AutoMQ deployment (#2562)

* feat(helm): add demo-values.yaml and update README for AutoMQ deployment

* fix(demo/readme): fix demo-values.yaml and change readme description

* fix(README): update Helm chart references to use 'kafka'

* feat(values): update demo-values.yaml and README for AutoMQ deployment

* fix(demo): image tag

* fix(readme): bitnami helm chart version

* fix(readme): bitnami helm chart version

* fix(docker): update Dockerfile for AutoMQ while github action packagi… (#2574)

fix(docker): update Dockerfile for AutoMQ while github action packaging installations and permissions
2025-05-19 10:52:56 +08:00
1sonofqiu f57de0ed0f
feat: cherry-pick 2560 (#2571)
feat(bitnami): init and cp bitnami kafka container 3.9.0 (#2560)

* feat(bitnami): init and cp bitnami kafka container 3.9.0

* refactor(bitnami): rename Bitnami container files for consistency
2025-05-16 19:53:21 +08:00
Xu Han@AutoMQ 813c6ec54c
chore(log): log readiness check result to stdout (#2568)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-16 16:06:40 +08:00
Gezi-lzq 5f2808d0b0
feat(docker): add Spark Iceberg Docker setup and demonstration notebook (#2567)
* feat(docker): add Spark Iceberg Docker setup and demonstration notebook (#2553)

* feat(docker): add Spark Iceberg Docker setup and demonstration notebook

* docs(readme): add Quick Start guide for AutoMQ Table Topic with Docker Compose

* fix(workflow): update Docker image tag for Spark Iceberg build (#2555)

* fix(workflow): remove conditional image build for main branch

* fix(workflow): specify image tag for Spark Iceberg Docker build

* fix(workflow): update Docker image tag for Spark Iceberg build

* fix(docker): update automq image tag to 1.5.0-rc0 in docker-compose.yml
2025-05-16 15:19:52 +08:00
Xu Han@AutoMQ eea5f0a94d
feat(config): remove client logs from stdout (#2565)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-16 15:13:10 +08:00
Xu Han@AutoMQ e89412ec14
fix(perf): unify the time in different nodes (#2554)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-15 18:56:19 +08:00
Yu Ning 59b3a21899
refactor(s3stream/object-wal): complete appends sequentially (#2549)
* refactor(s3stream/object-wal): complete appends sequentially (#2426)

* chore: add todos

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor(s3stream/object-wal): sequentially succeed

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor(s3stream/object-wal): drop discontinuous objects during recovery

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: introduce `MockObjectStorage`

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: test sequentially succeed

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: record endOffset in the object path

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: different version of wal object header

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: adapt to the new object header format

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: recover from the trim offset

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: test recover continuous objects from trim offset

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: test marshal and unmarshal wal object header

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: fix tests

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: test recover from discontinuous objects

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: test recover from v0 and v1 objects

Signed-off-by: Ning Yu <ningyu@automq.com>

* style: fix lint

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: fix tests

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-05-15 11:24:46 +08:00
Gezi-lzq 814ff0e2e6
fix(github-release): correct IS_LATEST variable usage in upload condi… (#2538)
fix(github-release): correct IS_LATEST variable usage in upload condition (#2537)
2025-05-14 17:09:46 +08:00
Yu Ning 8e1992d29a
fix(s3stream/wal): fix incorrect offset return value during recovery (#2543)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-05-14 17:08:52 +08:00
Shichao Nie fed831524d
fix(s3stream): add delayed deletion for S3 WAL (#2525)
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-05-13 18:48:33 +08:00
Xu Han@AutoMQ e4494409fd
fix(storage): fix upload wal rate missing (#2528)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-13 18:45:14 +08:00
Xu Han@AutoMQ 3343c0ed06
feat(zerozone): fast upload when snapshot-read enable (#2534)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-13 18:44:19 +08:00
Gezi-lzq 35279a0552
chore(test): increase timeout for write completion in AbstractObjectSto… (#2536)
fix(test): increase timeout for write completion in AbstractObjectStorageTest
2025-05-13 17:49:45 +08:00
Xu Han@AutoMQ 7a7cb7e842
feat(table): validate table config (#2523)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-13 16:13:26 +08:00
Shichao Nie a906f602f8
feat(cli): add default s3 wal config (#2512)
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-05-13 11:43:46 +08:00
Xu Han@AutoMQ e820ea4048
fix(test): fix e2e dependencies conflict (#2518) (#2519)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-12 16:40:42 +08:00
Xu Han@AutoMQ ddc8e3a6a7
feat(failover): add wal failover support (#2516) (#2517)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-09 15:43:20 +08:00
Xu Han@AutoMQ c9a97ebf19
feat(table_topic): add table topic support (#2511) (#2513)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-08 14:10:56 +08:00
Shichao Nie 09359077c8
fix(s3stream): fix backward compatibility of default aws credentials (#2510)
* fix(s3stream): fix backward compatibility of default aws credentials

Signed-off-by: Shichao Nie <niesc@automq.com>

* fix(s3stream): fix broken test

Signed-off-by: Shichao Nie <niesc@automq.com>

---------

Signed-off-by: Shichao Nie <niesc@automq.com>
2025-05-07 17:54:50 +08:00
Xu Han@AutoMQ fea145bfd8
feat(zerozone): support zero cross az traffic cost (#2506)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-05-06 15:54:52 +08:00
Gezi-lzq d041e13f9e
fix(storage): ensure proper reference counting for ByteBuf in write o… (#2499)
fix(storage): ensure proper reference counting for ByteBuf in write o… (#2452)

* fix(storage): ensure proper reference counting for ByteBuf in write operations

* feat(storage): implement fast retry mechanism and improve resource management in write operations

* test(storage): add concurrency test for write operations and ensure buffer release

* test(storage): add test for write permit acquisition and blocking behavior

* style(test): format code for consistency in AbstractObjectStorageTest

* feat(storage): add constructor for MemoryObjectStorage with concurrency support

* fix(storage): ensure proper release of ByteBuf resources in write operations

* chore: polish code

* fix(storage): improve error handling and resource management in write operations

* fix(storage): ensure proper release of resources on timeout in AbstractObjectStorage

* test(storage): increase timeout duration for resource cleanup assertions
2025-05-06 10:10:59 +08:00
Xu Han@AutoMQ 961ba10695
fix(snapshot_read): fix snapshot-read cache tryload trigger (#2460)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-28 19:12:22 +08:00
Xu Han@AutoMQ 887d5053e2
feat(snapshot_read): snapshot-read cache (#2453)
feat(snapshot_read): snapshot read cache

Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-28 14:25:55 +08:00
Xu Han@AutoMQ ddfadbea0d
fix(gradle): fix kafka-client conflict #2445 (#2446)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-23 18:11:06 +08:00
woshigaopp 8a152dd74c
feat(metrics): cherry pick add cert metrics 1.4 (#2439)
* feat: add cert metrics

* feat: check cert null

* feat: fix format

* feat: adjust cert metrics position

* feat: remove cert prefix
2025-04-22 15:14:33 +08:00
Xu Han@AutoMQ a67e45e1e3
feat(snapshot_read): support preferred node (#2436) (#2437)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-21 14:46:34 +08:00
daniel-y dc5b56b9ef
chore(license): use the apache license for the next major version (#2434)
Signed-off-by: daniel-y <daniel@automq.com>
2025-04-18 17:38:40 +08:00
Shichao Nie 6220570ca2
fix(s3stream): fix potential index leak on stream deletion (#2429)
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-04-18 11:41:34 +08:00
Xu Han@AutoMQ c775509bfd
fix(snapshot_read): prevent append (#2425)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-17 16:19:01 +08:00
Xu Han@AutoMQ e9ba7a8c71
feat(s3stream): add trigger wal upload interval (#2347) (#2423)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-17 12:00:16 +08:00
Xu Han@AutoMQ 0c98593176
feat(interceptor): extend client id (#2422)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-16 17:53:36 +08:00
Xu Han@AutoMQ d33bd4bbfc
feat(circuit): prevent unregister locked node (#2418) (#2419)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-16 11:22:47 +08:00
Xu Han@AutoMQ 022f2a7eb1
feat(circuit): add LocalFileObjectStorage storage limit (#2415) (#2417)
* feat(circuit): add LocalFileObjectStorage storage limit



* fix(cicuit): fix CR



---------

Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-15 19:52:05 +08:00
Yu Ning 303cd28732
perf(s3stream/wal): add append timeout in block WAL (#2399)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-04-11 14:17:09 +08:00
Xu Han@AutoMQ 1de161e4e6
feat(circuit): support node circuit breaker (#2409) (#2413)
* feat(circuit): add circuit object storage



* fix(circuit): fix code review comment



---------

Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-09 14:22:18 +08:00
Xu Han@AutoMQ 10ba1a633f
feat(multi_read): add get partitions api (#2338) (#2345)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-09 10:55:47 +08:00
Xu Han@AutoMQ c04275422f
feat(release): release 1.3.3 (#2412)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-08 16:17:01 +08:00
Xu Han@AutoMQ d3e09e1ad2
chore(test): add test timeout (#2411)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-04-08 15:05:13 +08:00
Yu Ning 3a92901eb3
perf(s3stream/objectstorage): unify the throttle criteria (#2396)
perf(s3stream/objectstorage): unify the throttle criteria (#2386)

* perf(s3stream/objectstorage): unify the throttle criteria



* refactor(s3stream/objectstorage): retry on 403 responses



* refactor(objectstorage): use `TimeoutException` instead of `ApiCallAttemptTimeoutException`



* refactor: supress the cause of `ObjectNotExistException`



* style: fix lint



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-04-02 10:52:27 +08:00
Xu Han@AutoMQ f88c72b158
feat(s3stream): support s3 write timeout (#2356) (#2361)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-03-18 15:58:59 +08:00
Xu Han@AutoMQ 87b63600c2
feat(release): bump version to 1.3.3-rc0 (#2355)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-03-17 19:57:49 +08:00
Shichao Nie dfa35b90b8
feat(linking): use linkId for update group API (#2352)
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-03-14 15:18:00 +08:00
Xu Han@AutoMQ 95a2107030
refactor(automq): rename producerouter to traffic interceptor (#2353)
refactor(automq): rename producerouter to traffic interceptor (#2350)

Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-03-14 15:07:15 +08:00
Shichao Nie 1610f2f4a5
refactor(core): refine config names for kafka linking (#2348) (#2349)
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-03-14 11:27:21 +08:00
Yu Ning 814644530c
refactor(controller): add method `ControllerServer#reconfigurables` (#2346)
refactor(controller): add method `ControllerServer#reconfigurables` (#2344)

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-03-13 11:12:47 +08:00
Yu Ning 94107fc1a9
fix(s3storage): release Bytebuf allocated by WAL earlier to prevent memory fragmentation (#2342)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-03-12 11:33:03 +08:00
Yu Ning c0f5a7de29
perf(s3stream): limit write traffic to object storage (#2335)
* chore(objectstorage): log next retry delay

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: a `TrafficLimiter` to limit the network traffic

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: a `TrafficMonitor` to monitor the network traffic

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: record success and failed write requests

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: queued pending write tasks

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: run write tasks one by one

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: use a `TrafficRegulator` to control the rate of write requests

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: limit the inflight force upload tasks

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix: correct retry count

Signed-off-by: Ning Yu <ningyu@automq.com>

* chore: fix commit object logs

Signed-off-by: Ning Yu <ningyu@automq.com>

* chore: log force uploads

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix: catch exceptions

Signed-off-by: Ning Yu <ningyu@automq.com>

* style: fix lint

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix: fix re-trigger run write task

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf: increate if no traffic

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: remove useless try-catch

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf: ensure only one inflight force upload tasks

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: move inner classes outside

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf: increase rate limit slower

Signed-off-by: Ning Yu <ningyu@automq.com>

* chore: add a prefix in `AbstractObjectStorage#logger`

Signed-off-by: Ning Yu <ningyu@automq.com>

* chore: reduce useless logs

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf: reduce the sample count on warmup

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: introduce `TrafficVolumeLimiter` base on IBM `AsyncSemaphore`

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: limit the inflight write requests

Signed-off-by: Ning Yu <ningyu@automq.com>

* chore: reduce useless logs

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix: fix release size

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix: release permits once the request failed

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf: increase to max after 2 hours

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix: limit the request size

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf: adjust constants

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-03-11 20:44:53 +08:00
Shichao Nie 19900d258e
feat(core): add kafka linking interface (#2336)
* feat(core): add kafka linking interface (#2289)

Signed-off-by: Shichao Nie <niesc@automq.com>

* fix(linking): fix npe on default implementation (#2306)

Signed-off-by: Shichao Nie <niesc@automq.com>

* feat(linking): add updateGroup interface (#2328)

* feat(core): rename kafka linking interface

Signed-off-by: Shichao Nie <niesc@automq.com>

* feat(core): adjust shutdown order

Signed-off-by: Shichao Nie <niesc@automq.com>

* feat(linking): add update group interface

Signed-off-by: Shichao Nie <niesc@automq.com>

---------

Signed-off-by: Shichao Nie <niesc@automq.com>

* feat(core): enable producer id modification in MutableRecordBatch (#2329)

Signed-off-by: Shichao Nie <niesc@automq.com>

* feat(core): add connection id param (#2334)

Signed-off-by: Shichao Nie <niesc@automq.com>

---------

Signed-off-by: Shichao Nie <niesc@automq.com>
2025-03-10 11:10:20 +08:00
Xu Han@AutoMQ 00dcea1738
feat(release): bump version to 1.3.2 (#2330)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-02-27 19:38:04 +08:00
Xu Han@AutoMQ 72009f1b60
fix(action): fix release action (#2324)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-02-19 21:02:10 +08:00
Shichao Nie 866088c70f
fix(s3stream): skip waiting for pending part on release (#2316) (#2319)
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-02-19 19:06:03 +08:00
Xu Han@AutoMQ 27aeefe056
fix(action): change upload bucket (#2313)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-02-19 14:49:32 +08:00
Yu Ning 60c2ff747a
fix(tool/perf): add admin properties in `ConsumerService#admin` (#2311)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-02-19 13:58:52 +08:00
Yu Ning e63bfc8f29
feat(tool/perf): add option "--common-config-file" (#2309)
feat(tool/perf): add option "--common-config-file" (#2308)

* refactor: rename "--common-configs" to "--admin-configs"



* feat: add option "--common-config-file"



* refactor: use `Properties` rather than `Map` to pass configs



* style: fix lint



* refactor: remove "--admin-config"



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-02-19 10:32:40 +08:00
Yu Ning 5cda6e4086
feat(s3stream/failover): check request epoch >= wal epoch (#2305)
feat(s3stream/failover): check request epoch >= wal epoch (#2302)

* chore(controller): more logs in `getOpeningStream`



* feat(s3stream/failover): check request epoch >= wal epoch



* test(s3stream/failover): test node epoch checker



* test: increase timeout



* chore: expose `QuorumController#streamControlManager`



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-02-14 18:39:36 +08:00
Yu Ning d21b058721
feat(s3stream/wal): add constraints in recovery mode (#2304)
feat(s3stream/wal): add constraints in recovery mode (#2301)

* feat(s3stream/wal): add constraints in recovery mode



* refactor: log it rather than throw an exception



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-02-14 18:39:18 +08:00
Yu Ning 2bef7e31ec
fix(s3stream/wal): increase max record size from 64MiB to 128MiB (#2299)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-02-14 12:04:36 +08:00
Yu Ning c077d513eb
fix(s3stream/wal): increase max record size from 16MiB to 64MiB (#2297)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-02-14 11:16:59 +08:00
Yu Ning 402c0a68f5
fix(network): adjust number of permits if the request is huge (#2295)
fix(network): adjust number of permits if the request is huge (#2294)

* refactor: use only one semaphore to limit the queues requests size



* fix(network): adjust number of permits if the request is huge



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-02-12 20:13:24 +08:00
Xu Han@AutoMQ 513e5845d1
fix(s3stream): halt the process when node is fenced (#2292)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-02-12 16:31:09 +08:00
Yu Ning 3f1ca24b26
fix(tools/perf): only delete test topics on reset (#2285)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-02-07 09:54:37 +08:00
Xu Han@AutoMQ 3235c490cf
chore(s3stream): replace eventloop with executor in async semaphore (#2283)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-02-06 10:49:04 +08:00
Xu Han@AutoMQ b4c5341e43
feat(s3stream): fast fail s3 request (#2281)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-02-06 10:02:13 +08:00
Shichao Nie aeb6c7d2f8 fix(core): add missing setting method
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-01-24 10:09:48 +08:00
Yu Ning 9eca0de19d refactor(controller): consider brokers that has recently `CONTROLLED_SHUTDOWN` as `SHUTTING_DOWN` (#2261)
* refactor(controller): consider brokers that has recently `CONTROLLED_SHUTDOWN` as `SHUTTING_DOWN`

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: test `BrokerHeartbeatManager#brokerState`

Signed-off-by: Ning Yu <ningyu@automq.com>

* revert(NodeState): revert `SHUTDOWN` and `SHUTTING_DOWN` to `FENCED` and `CONTROLLED_SHUTDOWN`

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-24 10:09:32 +08:00
Ning Yu 9f3b55b87a fix(tools/perf): fix option name "--max-consume-record-rate"
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-24 10:08:56 +08:00
Ning Yu d38e9301fe fix(s3stream/wal): check `isBlockDev` by prefix in some rare cases
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-21 21:02:53 +08:00
Yu Ning fd3e7122b9
feat(tools/perf): support to limit the max poll rate of consumers (#2271)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-13 17:10:31 +08:00
Shichao Nie 3fdef4c070
feat(config): fix exporter uri type (#2267)
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-01-10 15:21:12 +08:00
Shichao Nie 5860a396b7
fix(s3stream): fix compaction block on upload exception (#2264)
Signed-off-by: Shichao Nie <niesc@automq.com>
2025-01-10 10:21:24 +08:00
Xu Han@AutoMQ 273c134683
chore(version): bump version to 1.3.2-rc0 (#2260)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-01-08 10:47:59 +08:00
Xu Han@AutoMQ 62abf707e5
perf(produce): fix validate compressed records alloc too many memory (#2257)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-01-07 22:17:27 +08:00
Yu Ning cd7d337601
chore(version): bump version to 1.3.1 (#2254)
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-03 17:53:10 +08:00
Ning Yu 732b3f8d1c fix(cli/deploy): override "controller.quorum.bootstrap.servers"
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-03 14:00:53 +08:00
Xu Han@AutoMQ 8189d89e78 chore(config): change s3.stream.object.compaction.max.size.bytes default value from 1GiB to 10GiB (#2249)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-01-02 17:38:12 +08:00
Robin Han abba7c44b7 feat(table): support partition & upsert config
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2025-01-02 13:28:47 +08:00
Ning Yu bb2409d99d style: fix lint
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-02 12:39:55 +08:00
Ning Yu d233658268 feat: use the internal partitioner to choose the partition to send msg to
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-02 12:39:55 +08:00
Ning Yu 64b704ef5e feat: add a random string in the topic name
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-02 12:39:55 +08:00
Ning Yu 9462e42f25 feat: only delete topics created by the perf tool
Signed-off-by: Ning Yu <ningyu@automq.com>
2025-01-02 12:39:55 +08:00
Shichao Nie 9502ac3696
fix(s3stream): report compaction delay after two compaction period (#2242)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-12-24 17:14:19 +08:00
Gezi-lzq 50946bf210
feat(config): add table topic conversion type configuration (#2203) (#2240)
* feat(config): add table topic conversion type configurations

* feat(config): rename table topic type to schema type and update related configurations

* feat(config): add table topic schema registry URL configuration and validation

* test(config): add unit tests for ControllerConfigurationValidator table topic schema configuration

* fix(tests): update exception type in ControllerConfigurationValidatorTableTest for schema validation

* feat(config): polish code
2024-12-20 18:44:51 +08:00
Robin Han 6605e37dd3 ~
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-20 17:17:54 +08:00
Xu Han@AutoMQ 8125b80ed4 feat(tools/perf): support schema message perf (#2226)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-20 15:09:33 +08:00
Xu Han@AutoMQ 7e8776da63 chore(gradle): update aws version to 2.29.26 (#2210)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-20 15:09:33 +08:00
Xu Han@AutoMQ 9fe78c2aaa feat(table): auto create table topic control topic (#2186)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-20 15:09:33 +08:00
Xu Han@AutoMQ a1f9969773 chore(table): set table max.message.bytes to 20MiB (#2182)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-20 15:09:33 +08:00
Xu Han@AutoMQ 0fca069a12 chore(stream): move asyncsemaphore to util (#2173)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-20 15:09:33 +08:00
Xu Han@AutoMQ 2d6fe1f805 feat(table): table topic aspect 2024-12-20 15:09:33 +08:00
Ning Yu 9cb537a9b3 style: fix lint
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 609bcc6672 fix(backpressure): fix metric value of back pressure state (#2209)
fix: fix metric value of back pressure state

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 9e7fec1afc fix(backpressure): start before registering to dynamic configs (#2208)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 985596c44c feat(backpressure): stop and remove all scheduled tasks on shutdown (#2207)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 5e11479053 feat(backpressure): support dynamic configs (#2204)
* feat(backpressure): make back pressure manager configurable

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: test diabled

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: move backpressure from s3stream to kafka.core

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: init `BackPressureManager` in `BrokerServer`

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: introduce `BackPressureConfig`

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: make `BackPressureManager` reconfigurable

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: test reconfigurable

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: rename config key

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: move metric "back_pressure_state" from s3stream to core

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 37ae1766d9 feat(backpressure): add metrics (#2198)
* feat(backpressure): log it on recovery from backpressure

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: add metric fetch_limiter_waiting_task_num

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: add metric fetch_limiter_timeout_count

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: add metric fetch_limiter_time

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: add metric back_pressure_state

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: add metric broker_quota_limit

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix(backpressure): run checkers with fixed delay

Signed-off-by: Ning Yu <ningyu@automq.com>

* style: fix lint

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf: drop too large values

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor: record -1 for other states

Signed-off-by: Ning Yu <ningyu@automq.com>

* test: fix tests

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 8e004eea2d fix(quota): check whether the client in white list before fetch (#2181)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning d9245691b4 fix(quota): limit the max throttle time (#2180)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning b660d48fe1 feat(quota): exclude internal client IDs from broker quota (#2179)
* feat(quota): exclude internal client IDs from broker quota

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat(autobalancer): mark producers and consumers internal clients

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning df78978543 feat(quota): support to get current quota metric value... (#2170)
* fix: fix logs

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat(quota): support to get current quota metric value

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor(backpressure): remove `Regulator#minimize`

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf(quota): increase the max of broker quota throttle time

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf(backpressure): decrease cooldown time

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf(quota): increase the max of broker quota throttle time

Signed-off-by: Ning Yu <ningyu@automq.com>

* docs: update comments

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 42099b0b3f chore(backpressure): log it on back pressure (#2164)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning e8502b64b0 feat(quota): support to get current quota by type (#2163)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 4bfa8af6bd refactor(backpressure): introduce interface `Checker` (#2162)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 574c0ea4cc feat(backpressure): back pressure by system load (#2161)
* feat(backpressure): init backpressure module

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat(backpressure): implement `DefaultBackPressureManager`

Signed-off-by: Ning Yu <ningyu@automq.com>

* test(backpressure): test `DefaultBackPressureManager`

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning 5d2f4a4c5d feat(quota): support broker quota for slow fetch (#2160)
* feat(quota): introduce `SLOW_FETCH` broker quota

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat(quota): add slow fetch quota

Signed-off-by: Ning Yu <ningyu@automq.com>

* test(quota): test broker slow fetch quota

Signed-off-by: Ning Yu <ningyu@automq.com>

* test(quota): test zero quota value

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Yu Ning ef0ce42126 feat(quota): support to update broker request rate quota (#2158)
* refactor(quota): refactor `maybeRecordAndGetThrottleTimeMs`

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix(quota): throttle the produce request whatever the acks is

Signed-off-by: Ning Yu <ningyu@automq.com>

* refactor(quota): separate `Request` in `ClientQuotaManager` and `RequestRate` in `BrokerQuotaManager`

Signed-off-by: Ning Yu <ningyu@automq.com>

* sytle: fix lint

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat(quota): support to update broker request rate quota

Signed-off-by: Ning Yu <ningyu@automq.com>

* test(quota): test update quota

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-12-20 11:56:15 +08:00
Xu Han@AutoMQ 786d405caa
feat(version): bump to 1.3.1-rc0 (#2234)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-20 10:56:34 +08:00
Shichao Nie 48eeb81cec
fix(core): fix potential infinite recursion on reading empty segment (#2229)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-12-17 11:43:38 +08:00
Shichao Nie d79d08ab4a
feat(telemetry): support gzip compression on uploading metrics & logs… (#2222)
feat(telemetry): support gzip compression on uploading metrics & logs to s3

Signed-off-by: Shichao Nie <niesc@automq.com>
2024-12-13 16:39:41 +08:00
Xu Han@AutoMQ 080bae4d07
feat(tools/perf): support schema message perf (#2227)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-13 14:31:46 +08:00
Xu Han@AutoMQ 504761b7dd
fix(tools/perf): fix the perf tools await count (#2220)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-12 10:00:07 +08:00
Xu Han@AutoMQ f6936cadab
feat(table): enhance command utils (#2217)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-06 14:39:40 +08:00
Xu Han@AutoMQ 7bf19db2c8
fix(docker): fix docker compose quick start (#2212)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-12-05 18:48:41 +08:00
Shichao Nie 0bac03ed48
feat(version): bump version to 1.3.0-rc2 (#2211)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-12-05 15:32:35 +08:00
Shichao Nie 78cb2cb508
fix(core): write next node id into image (#2205)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-12-03 10:37:03 +08:00
Shichao Nie 1966e042ff
fix(core): fix getting duplicated node id (#2199)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-12-02 14:49:31 +08:00
Xu Han@AutoMQ 7a12c17e5d
fix(issues2193): retry 2 times to cover most of BlockNotContinuousException (#2195)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-11-29 19:57:27 +08:00
Yu Ning a14f07e8b7
fix: use the "adjusted" `maxSize` in `ElasticLogSegment#readAsync` (#2190)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-28 10:54:20 +08:00
Yu Ning cc34841c49
fix: release `PooledMemoryRecords` if it's dropped in the fetch session (#2187)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-28 10:53:21 +08:00
Shichao Nie f6f5412c76
feat(core): reuse unregistered node when requesting for next node id (#2183)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-27 17:59:40 +08:00
Yu Ning f7e8b9abb3
fix(stream): release `FetchResult`s if the subsequent fetch fails (#2174)
fix(stream): release `FetchResult`s if the subsequent fetch fails (#2172)

* fix(stream): release `FetchResult`s if the subsequent fetch fails



* revert: "fix(stream): release `FetchResult`s if the subsequent fetch fails"

This reverts commit 5836a6afa0.

* refactor: add the `FetchResult` into the list in order rather than in reverse order



* fix: release `FetchResult`s if failed to fetch



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-25 09:53:06 +08:00
Shichao Nie bf8ebdb098
feat(version): bump version to 1.3.0-rc1 (#2178)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-25 09:46:24 +08:00
Shichao Nie 67347165ca
fix(issue2151): avoid using stale broker IPs for AutoBalancer consume… (#2177)
fix(issue2151): avoid using stale broker IPs for AutoBalancer consumer (#2152)

close #2151

Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-23 21:07:08 +08:00
Xu Han@AutoMQ a2ab79deae
chore(workflow): add spotless check (#2169)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-11-21 18:50:56 +08:00
Yu Ning 82a76e87e0
feat(tools/perf): create topics in batch (#2165)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-20 15:59:45 +08:00
Yu Ning 7d0f2a2746
chore(github): update code owners (#2156)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-13 17:33:00 +08:00
Xu Han@AutoMQ e0c761c110
feat(version): bump version to 1.3.0-rc0 (#2153)
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-11-11 21:48:19 +08:00
Xu Han@AutoMQ c091ba740f
Merge pull request #2150 from AutoMQ/merge_3.9
feat(merge): merge apache kafka 3.9.0 cc53a63
2024-11-08 15:43:31 +08:00
Shichao Nie 504b0b1cb2
fix(issue2140): remove override equals and hashCode method for ObjectReader (#2148)
close #2140

Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-08 15:33:13 +08:00
Robin Han aa856f83d9
Merge branch '3.9.0' into merge_3.9 2024-11-08 15:30:15 +08:00
Shichao Nie 1ada92c329
fix(issue2139): add computeIfAbsent atomic operation to AsyncLRUCache (#2145)
close #2139

Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-08 15:00:39 +08:00
Shichao Nie 8be9e519c7
fix(issue2139): prevent read object info from closed ObjectReader (#2143)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-08 14:02:22 +08:00
Yu Ning 0a851c3047
feat(tools/perf): run benchmark without consumer (#2134)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-08 11:14:15 +08:00
Yu Ning fb5bce8291
fix(s3stream/storage): correct if condition on `awaitTermination` (#2137)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-08 11:13:26 +08:00
Yu Ning 37e1af586d
refactor(tools/perf): retry sending messages in when waiting topics ready (#2132)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-07 15:41:31 +08:00
Yu Ning cab9e191da
perf(log): avoid too many checkpoint at the same time (#2129)
Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-07 14:10:38 +08:00
Yu Ning c243eaf9ca
perf(tools/perf): assuming all partitions have the same offset at the same time (#2127)
* feat(tools/perf): log progress on resetting offsets

Signed-off-by: Ning Yu <ningyu@automq.com>

* fix: reset timeouts

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: increase the log interval

Signed-off-by: Ning Yu <ningyu@automq.com>

* perf(tools/perf): assuming all partitions have the same offset at the same time

Signed-off-by: Ning Yu <ningyu@automq.com>

* feat: limit the min of --backlog-duration

Signed-off-by: Ning Yu <ningyu@automq.com>

---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-07 10:10:28 +08:00
Colin P. McCabe cc53a632ed Bump version to 3.9.0 2024-11-06 13:15:24 -08:00
Yu Ning 7f9a63195c
revert(s3stream/limiter): increase the max tokens of network limiters (#2125)
This reverts commit bc63e6b614.
2024-11-06 16:53:23 +08:00
Shichao Nie c32772f588
fix(e2e): remove unstable autobalancer tests (#2123)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-06 15:54:54 +08:00
Shichao Nie aafef77114
fix(checkstyle): fix checkstyle (#2121)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-06 11:01:11 +08:00
Shichao Nie dec14165fb
fix(metrics): fix metrics name for BrokerTopicPartitionMetrics (#2118)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-05 20:20:43 +08:00
Shichao Nie fcd0d5e529
fix(s3stream): fix available bandwidth metrics (#2120)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-05 20:20:00 +08:00
Shichao Nie 9b4db72a0d
fix(compaction): prevent double release on compaction shutdown (#2116)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-05 11:22:26 +08:00
ShivsundarR 4a562cddcb
Removed Set.of usage (#17683)
Reviewers: Federico Valeri <fedevaleri@gmail.com>, Lianet Magrans <lmagrans@confluent.io>, Chia-Ping Tsai <chia7712@gmail.com>
2024-11-04 20:04:39 +01:00
Xu Han@AutoMQ c92bedb6a9
fix(s3stream): wait force upload complete before return (#2113)
Signed-off-by: Shichao Nie <niesc@automq.com>
Co-authored-by: Shichao Nie <niesc@automq.com>
2024-11-03 22:30:40 +08:00
Shichao Nie c51cc0298a
fix(issue2108): avoid blocking at the end of a compaction iteration when there are un-uploaded data (#2111)
Signed-off-by: Shichao Nie <niesc@automq.com>
2024-11-03 16:14:38 +08:00
Yu Ning e9b2117b38
perf: limit the inflight requests (#2100) (#2106)
* docs: add todos



* perf(network): limit the inflight requests by size



* perf(ReplicaManager): limit the queue size of the `fetchExecutor`s



* perf(KafkaApis): limit the queue size of async request handlers



* refactor(network): make "queued.max.requests.size.bytes" configurable



* style: fix lint



* fix(network): limit the min queued request size per queue



---------

Signed-off-by: Ning Yu <ningyu@automq.com>
2024-11-01 19:30:39 +08:00
Jonah Hooper bcb5d167fd [KAFKA-17870] Fail CreateTopicsRequest if total number of partitions exceeds 10k (#17604)
We fail the entire CreateTopicsRequest action if there are more than 10k total
partitions being created in this topic for this specific request. The usual pattern for
this API to try and succeed with some topics. Since the 10k limit applies to all topics
then no topic should be created if they all exceede it.

Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-10-31 13:55:49 -07:00
Xu Han@AutoMQ 72dfbc32f5
Merge pull request #2105 from AutoMQ/merge_3_9
feat(merge): merge apache kafka 3.9 398b4c4fa1
2024-10-31 17:26:03 +08:00
Robin Han 0e84ac7de2
fix(merge): fix unit test
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-10-31 17:11:23 +08:00
Robin Han 7e759baf40
fix(merge): fix automq version
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-10-31 16:04:47 +08:00
Robin Han 2854533f42
fix(merge): fix compile error
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-10-31 12:02:17 +08:00
Robin Han fbd0c7ce3e
fix(merge): fix conflict
Signed-off-by: Robin Han <hanxvdovehx@gmail.com>
2024-10-31 11:25:36 +08:00
Colin Patrick McCabe 398b4c4fa1 KAFKA-17868: Do not ignore --feature flag in kafka-storage.sh (#17597)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, Justine Olshan <jolshan@confluent.io>
2024-10-25 17:08:51 -07:00
Colin Patrick McCabe c821449fb7 KAFKA-17794: Add some formatting safeguards for KIP-853 (#17504)
KIP-853 adds support for dynamic KRaft quorums. This means that the quorum topology is
no longer statically determined by the controller.quorum.voters configuration. Instead, it
is contained in the storage directories of each controller and broker.

Users of dynamic quorums must format at least one controller storage directory with either
the --initial-controllers or --standalone flags.  If they fail to do this, no quorum can be
established. This PR changes the storage tool to warn about the case where a KIP-853 flag has
not been supplied to format a KIP-853 controller. (Note that broker storage directories
can continue to be formatted without a KIP-853 flag.)

There are cases where we don't want to specify initial voters when formatting a controller. One
example is where we format a single controller with --standalone, and then dynamically add 4
more controllers with no initial topology. In this case, we want the 4 later controllers to grab
the quorum topology from the initial one. To support this case, this PR adds the
--no-initial-controllers flag.

Reviewers: José Armando García Sancio <jsancio@apache.org>, Federico Valeri <fvaleri@redhat.com>
2024-10-21 10:41:26 -07:00
Federico Valeri 7842e25d32 KAFKA-17031: Make RLM thread pool configurations public and fix default handling (#17499)
According to KIP-950, remote.log.manager.thread.pool.size should be marked as deprecated and replaced by two new configurations: remote.log.manager.copier.thread.pool.size and remote.log.manager.expiration.thread.pool.size. Fix default handling so that -1 works as expected.

Reviewers: Luke Chen <showuon@gmail.com>, Gaurav Narula <gaurav_narula2@apple.com>, Satish Duggana <satishd@apache.org>, Colin P. McCabe <cmccabe@apache.org>
2024-10-21 10:39:53 -07:00
Josep Prat de9a7199df KAFKA-17810 upgrade Jetty because of CVE-2024-8184 (#17517)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-10-21 02:51:37 +08:00
Colin Patrick McCabe abd4bf08ab
KAFKA-17790: Document that control.plane.listener should be removed before ZK migration is finished (#17501)
Reviewers: Luke Chen <showuon@gmail.com>
2024-10-15 14:36:16 -07:00
Colin P. McCabe 796ce2121b KAFKA-17788: During ZK migration, always include control.plane.listener.name in advertisedBrokerListeners
During ZK migration, always include control.plane.listener.name in advertisedBrokerListeners, to be
bug-compatible with earlier Apache Kafka versions that ignored this misconfiguration. (Just as
before, control.plane.listener.name is not supported in KRaft mode itself.)

Reviewers: Luke Chen <showuon@gmail.com>
2024-10-15 14:34:42 -07:00
Ken Huang 51253e2bf4
KAFKA-17520 align the low bound of ducktape version (#17481)
Reviewers: Colin Patrick McCabe <cmccabe@apache.org>, Chia-Ping Tsai <chia7712@gmail.com>
2024-10-15 00:15:59 +08:00
David Arthur 8c3c6c3841
KAFKA-17193: Pin all external GitHub Actions to the specific git hash (#16960) (#17461)
Co-authored-by: Mickael Maison <mimaison@users.noreply.github.com>

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, Colin P. McCabe <cmccabe@apache.org>
2024-10-10 13:20:49 -07:00
Mickael Maison 44f15cc22c KAFKA-17749: Fix Throttler metrics name
Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-10-10 09:20:14 -07:00
PoAn Yang 4878174b77 KAFKA-16972 Move BrokerTopicMetrics to org.apache.kafka.storage.log.metrics (#16387)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-10-10 09:15:21 -07:00
Apoorv Mittal db4c80a455 KAFKA-17731: Removed timed waiting signal for client telemetry close (#17431)
Reviewers: Andrew Schofield <aschofield@confluent.io>, Kirk True <ktrue@confluent.io>, Chia-Ping Tsai <chia7712@gmail.com>, Lianet Magrans <lmagrans@confluent.io>
2024-10-10 08:55:43 -07:00
Colin Patrick McCabe bf95a3239c
KAFKA-17753: Update protobuf and commons-io dependencies (#17436)
Reviewers: Josep Prat <jlprat@apache.org>
2024-10-09 16:34:26 -07:00
Gaurav Narula ab6dafaab6 KAFKA-17751; fix pollTimeout calculation in pollFollowerAsVoter (#17434)
KAFKA-16534 introduced a change to send UpdateVoterRequest every "3 * fetchTimeoutMs" if the voter's configure endpoints are different from the endpoints persisted in the KRaft log. It also introduced a regression where if the voter nodes do not need an update then updateVoterTimer wasn't reset. This resulted in a busy-loop in KafkaRaftClient#poll method resulting in high CPU usage.

This PR modifies the conditions in pollFollowerAsVoter to reset updateVoterTimer appropriately.

Reviewers: José Armando García Sancio <jsancio@apache.org>
2024-10-09 18:13:10 -04:00
Colin Patrick McCabe 8af063a165
KAFKA-17735: release.py must not use home.apache.org (#17421)
Previously, Apache Kafka was uploading release candidate (RC) artifacts
to users' home directories on home.apache.org. However, since this
resource has been decommissioned, we need to follow the standard
approach of putting release candidate artifacts into the appropriate
subversion directory, at https://dist.apache.org/repos/dist/dev/kafka/.

Reviewers: Justine Olshan <jolshan@confluent.io>
2024-10-08 15:40:27 -07:00
Colin Patrick McCabe 0a70c3a61e
KAFKA-17714 Fix StorageToolTest.scala to compile under Scala 2.12 (#17400)
Reviewers: David Arthur <mumrah@gmail.com>, Justine Olshan <jolshan@confluent.io>, Chia-Ping Tsai <chia7712@gmail.com>
2024-10-08 11:00:18 +08:00
David Arthur 1d54a7373c KAFKA-17146 Include note to remove migration znode (#16770)
When reverting the ZK migration, we must also remove the /migration ZNode in order to allow the migration to be re-attempted in the future.

Reviewers: Colin P. McCabe <cmccabe@apache.org>, Chia-Ping Tsai <chia7712@gmail.com>
2024-10-07 10:34:22 -07:00
José Armando García Sancio 550bf60460 KAFKA-16927; Handle expanding leader endpoints (#17363)
When a replica restarts in the follower state it is possible for the set of leader endpoints to not match the latest set of leader endpoints. Voters will discover the latest set of leader endpoints through the BEGIN_QUORUM_EPOCH request. This means that KRaft needs to allow for the replica to transition from Follower to Follower when only the set of leader endpoints has changed.

Reviewers: Colin P. McCabe <cmccabe@apache.org>, Alyssa Huang <ahuang@confluent.io>
2024-10-04 14:53:17 +00:00
Alyssa Huang 5c95a5da31 MINOR: Fix kafkatest advertised listeners (#17294)
Followup for #17146

Reviewers: Bill Bejeck <bbejeck@apache.org>
2024-10-01 17:21:28 +00:00
Bill Bejeck edd77c1e25 MINOR: Need to split the controller bootstrap servers on ',' in list comprehenson (#17183)
Kafka Streams system tests were failing with this error:

Failed to parse host name from entry 3001@d for the configuration controller.quorum.voters.  Each entry should be in the form `{id}@{host}:{port}`.

The cause is that in kafka.py line 876, we create a delimited string from a list comprehension, but the input is a string itself, so each character gets appended vs. the bootstrap server string of host:port. To fix this, this PR adds split(',') to controller_quorum_bootstrap_servers. Note that this only applies when dynamicRaftQuorum=False

Reviewers: Alyssa Huang <ahuang@confluent.io>, Chia-Ping Tsai <chia7712@gmail.com>
2024-10-01 17:07:26 +00:00
David Arthur 2cbc5bd3ca KAFKA-17636 Fix missing SCRAM bootstrap records (#17305)
Fixes a regression introduced by #16669 which inadvertently stopped processing SCRAM arguments from kafka-storage.sh

Reviewers: Colin P. McCabe <cmccabe@apache.org>, Federico Valeri <fedevaleri@gmail.com>
2024-09-28 10:04:29 -04:00
Alyssa Huang 89cb632acd KAFKA-17608, KAFKA-17604, KAFKA-16963; KRaft controller crashes when active controller is removed (#17146)
This change fixes a few issues.

KAFKA-17608; KRaft controller crashes when active controller is removed
When a control batch is committed, the quorum controller currently increases the last stable offset but fails to create a snapshot for that offset. This causes an issue if the quorum controller renounces and needs to revert to that offset (which has no snapshot present). Since the control batches are no-ops for the quorum controller, it does not need to update its offsets for control records. We skip handle commit logic for control batches.

KAFKA-17604; Describe quorum output missing added voters endpoints
Describe quorum output will miss endpoints of voters which were added via AddRaftVoter. This is due to a bug in LeaderState's updateVoterAndObserverStates which will pull replica state from observer states map (which does not include endpoints). The fix is to populate endpoints from the lastVoterSet passed into the method.

Reviewers: José Armando García Sancio <jsancio@apache.org>, Colin P. McCabe <cmccabe@apache.org>, Chia-Ping Tsai <chia7712@apache.org>
2024-09-26 18:04:05 +00:00
Alyssa Huang c2c2dd424b KAFKA-16963: Ducktape test for KIP-853 (#17081)
Add a ducktape system test for KIP-853 quorum reconfiguration, including adding and removing voters.

Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-09-26 18:03:56 +00:00
Colin Patrick McCabe 57b098c397 KAFKA-17584: Fix incorrect synonym handling for dynamic log configurations (#17258)
Several Kafka log configurations in have synonyms. For example, log retention can be configured
either by log.retention.ms, or by log.retention.minutes, or by log.retention.hours. There is also
a faculty in Kafka to dynamically change broker configurations without restarting the broker. These
dynamically set configurations are stored in the metadata log and override what is in the broker
properties file.

Unfortunately, these two features interacted poorly; there was a bug where the dynamic log
configuration update code ignored synonyms. For example, if you set log.retention.minutes and then
reconfigured something unrelated that triggered the LogConfig update path, the retention value that
you had configured was overwritten.

The reason for this was incorrect handling of synonyms. The code tried to treat the Kafka broker
configuration as a bag of key/value entities rather than extracting the correct retention time (or
other setting with overrides) from the KafkaConfig object.

Reviewers: Luke Chen <showuon@gmail.com>, Jun Rao <junrao@gmail.com>, Kamal Chandraprakash<kamal.chandraprakash@gmail.com>, Christo Lolov <lolovc@amazon.com>, Federico Valeri <fedevaleri@gmail.com>, Rajini Sivaram <rajinisivaram@googlemail.com>, amangandhi94 <>
2024-09-26 14:20:33 +08:00
José Armando García Sancio e36c82d71c MINOR: Replace gt and lt char with html encoding (#17235)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-09-24 17:07:16 +00:00
Ken Huang 333483a16e MINOR: add a space for kafka.metrics.polling.interval.secs description (#17256)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-09-24 21:52:16 +08:00
TengYao Chi 7d14cd6b33 KAFKA-17459 Stablize reassign_partitions_test.py (#17250)
This test expects that each partition can receive the record, so using a non-null key helps distribute the records more randomly.

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-09-24 17:37:03 +08:00
Jakub Scholz 83091994a6 KAFKA-17543: Improve and clarify the error message about generated broker IDs in migration (#17210)
This PR tries to improve the error message when broker.id is set to -1 and ZK migration is enabled. It is not
needed to disable the broker.id.generation.enable option. It is sufficient to just not use it (by not setting
the broker.id to -1).

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, Luke Chen <showuon@gmail.com>
2024-09-18 11:47:01 -07:00
José Armando García Sancio c141acb6bf KAFKA-17048; Update docs for KIP-853 (#17076)
Change the configurations under config/kraft to use controller.quorum.bootstrap.servers instead of controller.quorum.voters. Add comments explaining how to use the older static quorum configuration where appropriate.

In docs/ops.html, remove the reference to "tentative timelines for ZooKeeper removal" and "Tiered storage is considered as an early access feature" since they are no longer up-to-date. Add KIP-853 information.

In docs/quickstart.html, move the ZK instructions to be after the KRaft instructions. Update the KRaft instructions to use KIP-853.

In docs/security.html, add an explanation of --bootstrap-controller and document controller.quorum.bootstrap.servers instead of controller.quorum.voters.

Reviewers: Mickael Maison <mickael.maison@gmail.com>, Alyssa Huang <ahuang@confluent.io>, Colin P. McCabe <cmccabe@apache.org>
2024-09-18 11:34:12 -07:00
Colin Patrick McCabe 389a8d8dec
Revert "KAFKA-16803: Change fork, update ShadowJavaPlugin to 8.1.7 (#16295)" (#17218)
This reverts commit 391778b8d7.

Unfortunately that commit re-introduced bug #15127 which prevented the publishing of kafka-clients
artifacts to remote maven. As that bug says:

    The issue triggers only with publishMavenJavaPublicationToMavenRepository due to signing.
    Generating signed asc files error out for shadowed release artifacts as the module name
    (clients) differs from the artifact name (kafka-clients).

    The fix is basically to explicitly define artifact of shadowJar to signing and publish plugin.
    project.shadow.component(mavenJava) previously outputs the name as client-<version>-all.jar
    though the classifier and archivesBaseName are already defined correctly in :clients and
    shadowJar construction.

Reviewers: David Arthur <mumrah@gmail.com>
2024-09-17 12:05:25 -07:00
Colin Patrick McCabe f324ef461f
MINOR: update documentation link to 3.9 (#17216)
Reviewers: David Arthur <mumrah@gmail.com>
2024-09-17 07:36:08 -07:00
Colin Patrick McCabe a1a4389c35 KAFKA-17543: Enforce that broker.id.generation.enable is not used when migrating to KRaft (#17192)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, David Arthur <mumrah@gmail.com>
2024-09-13 17:25:26 -07:00
Matthias J. Sax b43439482d KAFKA-17527: Fix NPE for null RecordContext (#17169)
Reviewers: Bruno Cadonna <bruno@confluent.io>
2024-09-13 16:32:54 -07:00
Colin Patrick McCabe 7d3ba8a0eb KAFKA-16468: verify that migrating brokers provide their inter.broker.listener (#17159)
When brokers undergoing ZK migration register with the controller, it should verify that they have
provided a way to contact them via their inter.broker.listener. Otherwise the migration will fail
later on with a more confusing error message.

Reviewers: David Arthur <mumrah@gmail.com>
2024-09-13 09:18:43 -07:00
Bruno Cadonna 4f0675d5e9 KAFKA-17489: Do not handle failed tasks as tasks to assign (#17115)
Failed tasks discovered when removed from the state updater during assignment or revocation are added to the task registry. From there they are retrieved and handled as normal tasks. This leads to a couple of IllegalStateExceptions because it breaks some invariants that ensure that only good tasks are assigned and processed.

This commit solves this bug by distinguish failed from non-failed tasks in the task registry.

Reviewer: Lucas Brutschy <lbrutschy@confluent.io>
2024-09-13 12:16:19 +02:00
David Arthur 4734077f47 KAFKA-17506 KRaftMigrationDriver initialization race (#17147)
There is a race condition between KRaftMigrationDriver running its first poll() and being notified by Raft about a leader change. If onControllerChange is called before RecoverMigrationStateFromZKEvent is run, we will end up getting stuck in the INACTIVE state.

This patch fixes the race by enqueuing a RecoverMigrationStateFromZKEvent from onControllerChange if the driver has not yet initialized. If another RecoverMigrationStateFromZKEvent was already enqueued, the second one to run will just be ignored.

Reviewers: Luke Chen <showuon@gmail.com>
2024-09-11 10:42:16 -04:00
Vikas Singh 5a4d2b44d2 MINOR: Few cleanups
Reviewers: Manikumar Reddy <manikumar.reddy@gmail.com>
2024-09-11 14:40:10 +05:30
David Arthur 6d3e77533e KAFKA-15793 Fix ZkMigrationIntegrationTest#testMigrateTopicDeletions (#17004)
Reviewers: Igor Soarez <soarez@apple.com>, Ajit Singh <>
2024-09-10 13:06:13 -07:00
xijiu f7fe4b9441 KAFKA-17458 Add 3.8 to transactions_upgrade_test.py, transactions_mixed_versions_test.py, and kraft_upgrade_test.py (#17084)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-09-11 02:13:01 +08:00
Ken Huang e885daf0e3 KAFKA-17492 skip features with minVersion of 0 instead of replacing 0 with 1 when BrokerRegistrationRequest < 4 (#17128)
The 3.8 controller assumes the unknown features have min version = 0, but KAFKA-17011 replace the min=0 by min=1 when BrokerRegistrationRequest < 4. Hence, to support upgrading from 3.8.0 to 3.9, this PR changes the implementation of ApiVersionsResponse (<4) and BrokerRegistrationRequest (<4) to skip features with supported minVersion of 0 instead of replacing 0 with 1

Reviewers: Jun Rao <junrao@gmail.com>, Colin P. McCabe <cmccabe@apache.org>, Chia-Ping Tsai <chia7712@gmail.com>
2024-09-11 01:17:41 +08:00
TengYao Chi 4b6437e6a5
KAFKA-17497 Add e2e for zk migration with old controller (#17131)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-09-10 12:14:47 +08:00
David Arthur 034780dce9 KAFKA-15648 Update leader volatile before handleLeaderChange in LocalLogManager (#17118)
Update the leader before calling handleLeaderChange and use the given epoch in LocalLogManager#prepareAppend. This should hopefully fix several flaky QuorumControllerTest tests.

Reviewers: José Armando García Sancio <jsancio@apache.org>
2024-09-06 14:23:13 -04:00
David Arthur d067ed0a2f
KAFKA-17457 Don't allow ZK migration to start without transactions (#17094)
This patch raises the minimum MetadataVersion for migrations to 3.6-IV1 (metadata transactions). This is only enforced on the controller during bootstrap (when the log is empty). If the log is not empty on controller startup, as in the case of a software upgrade, we allow the migration to continue where it left off.

The broker will log an ERROR message if migrations are enabled and the IBP is not at least 3.6-IV1.

Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-09-06 13:28:53 -04:00
Sebastien Viale 74d55ca639 KAFKA-16448: Add timestamp to error handler context (#17054)
Part of KIP-1033.

Co-authored-by: Dabz <d.gasparina@gmail.com>
Co-authored-by: loicgreffier <loic.greffier@michelin.com>

Reviewers: Matthias J. Sax <matthias@confluent.io>
2024-09-05 08:40:22 -07:00
Luke Chen 3cabf333ce MINOR: Update doc for tiered storage GA (#17088)
Reviewers: Satish Duggana <satishd@apache.org>
2024-09-05 19:19:33 +08:00
TengYao Chi 14e1ebee9e KAFKA-17454 Fix failed transactions_mixed_versions_test.py when running with 3.2 (#17067)
why df04887ba5 does not fix it?

The fix of df04887ba5 is to NOT collect the log from path `/mnt/kafka/kafka-operational-logs/debug/xxxx.log`if the task is successful. It does not change the log level. see ducktape b2ad7693f2/ducktape/tests/test.py (L181)

why df04887ba5 does not see the error of "sort"

df04887ba5 does NOT show the error since the number of features is only "one" (only metadata.version). Hence, the bug is not triggered as it does not need to "sort". Now, we have two features - metadata.version and krafe.version - so the sort is executed and then we see the "hello bug"

why we should change the kafka.log_level to INFO?

the template of log4j.properties is controlled by `log_level` (https://github.com/apache/kafka/blob/trunk/tests/kafkatest/services/kafka/templates/log4j.properties#L16), and the bug happens in writing debug message (e4ca066680/core/src/main/scala/kafka/server/metadata/BrokerMetadataListener.scala (L274)). Hence, changing the log level to DEBUG can avoid triggering the bug.

Reviewers: Justine Olshan <jolshan@confluent.io>, Chia-Ping Tsai <chia7712@gmail.com>
2024-09-04 15:56:11 +08:00
Justine Olshan 4756402f75 MINOR: Reduce log levels for transactions_mixed_versions_test with 3.2 due to bug in that version (#16787)
7496e62434 fixed an error that caused an exception to be thrown on broker startup when debug logs were on. This made it to every version except 3.2. 

The Kraft upgrade tests totally turn off debug logs, but I think we only need to remove them for the broken version.

Note: this bug is also present in 3.1, but there is no logging on startup like in subsequent versions.

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, David Jacot <david.jacot@gmail.com>
2024-09-04 15:56:04 +08:00
Luke Chen 28f5ff6039 KAFKA-17412: add doc for `unclean.leader.election.enable` in KRaft (#17051)
Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-09-03 16:12:20 -07:00
PoAn Yang a954ad1c67 KAFKA-17331 Throw unsupported version exception if the server does NOT support EarliestLocalSpec and LatestTieredSpec (#16873)
Add the version check to server side for the specific timestamp:
- the version must be >=8 if timestamp=-4L
- the version must be >=9 if timestamp=-5L

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-09-01 21:14:27 +08:00
Colin Patrick McCabe c7cc4d0b68 KAFKA-17434: Do not test impossible scenarios in upgrade_test.py (#17024)
Because of KIP-902 (Upgrade Zookeeper version to 3.8.2), it is not possible to upgrade from a Kafka version
earlier than 2.4 to a version later than 2.4. Therefore, we should not test these upgrade scenarios
in upgrade_test.py. They do happen to work sometimes, but only in the trivial case where we don't
create topics or make changes during the upgrade (which would reveal the ZK incompatibility).
Instead, we should test only supported scenarios.

Reviewers: Reviewers: José Armando García Sancio <jsancio@gmail.com>
2024-08-29 12:53:48 -07:00
Krishna Agarwal d9ebb2e79b
MINOR: Add experimental message for the native docker image (#17041)
The docker image for Native Apache Kafka was introduced with KIP-974 and was first release with 3.8 AK release.
The docker image for Native Apache Kafka is currently intended for local development and testing purposes.

This PR intends to add a logline indicating the same during docker image startup.

Reviewers: Manikumar Reddy <manikumar.reddy@gmail.com>
2024-08-29 17:33:11 +05:30
Luke Chen fbaea5ff6a KAFKA-17062: handle dangling "copy_segment_start" state when deleting remote logs (#16959)
The COPY_SEGMENT_STARTED state segments are counted when calculating remote retention size. This causes unexpected retention size breached segment deletion. This PR fixes it by
  1. only counting COPY_SEGMENT_FINISHED and DELETE_SEGMENT_STARTED state segments when calculating remote log size.
  2. During copy Segment, if we encounter errors, we will delete the segment immediately.
  3. Tests added.

Co-authored-by: Guillaume Mallet <>

Reviewers: Kamal Chandraprakash<kamal.chandraprakash@gmail.com>, Satish Duggana <satishd@apache.org>, Guillaume Mallet <>
2024-08-29 14:10:30 +08:00
Colin Patrick McCabe 7356328f53 KAFKA-12670: Support configuring unclean leader election in KRaft (#16866)
Previously in KRaft mode, we could request an unclean leader election for a specific topic using
the electLeaders API. This PR adds an additional way to trigger unclean leader election when in
KRaft mode via the static controller configuration and various dynamic configurations.

In order to support all possible configuration methods, we have to do a multi-step configuration
lookup process:

1. check the dynamic topic configuration for the topic.
2. check the dynamic node configuration.
3. check the dynamic cluster configuration.
4. check the controller's static configuration.

Fortunately, we already have the logic to do this multi-step lookup in KafkaConfigSchema.java.
This PR reuses that logic. It also makes setting a configuration schema in
ConfigurationControlManager mandatory. Previously, it was optional for unit tests.

Of course, the dynamic configuration can change over time, or the active controller can change
to a different one with a different configuration. These changes can make unclean leader
elections possible for partitions that they were not previously possible for. In order to address
this, I added a periodic background task which scans leaderless partitions to check if they are
eligible for an unclean leader election.

Finally, this PR adds the UncleanLeaderElectionsPerSec metric.

Co-authored-by: Luke Chen showuon@gmail.com

Reviewers: Igor Soarez <soarez@apple.com>, Luke Chen <showuon@gmail.com>
2024-08-28 14:14:41 -07:00
Dmitry Werner a4728f566f KAFKA-15746: KRaft support in ControllerMutationQuotaTest (#16620)
Reviewers: Mickael Maison <mickael.maison@gmail.com>
2024-08-28 14:14:34 -07:00
kevin-wu24 e5f47ba350 KAFKA-15406: Add the ForwardingManager metrics from KIP-938 (#16904)
Implement the remaining ForwardingManager metrics from KIP-938: Add more metrics for measuring KRaft performance:

kafka.server:type=ForwardingManager,name=QueueTimeMs.p99
kafka.server:type=ForwardingManager,name=QueueTimeMs.p999
kafka.server:type=ForwardingManager,name=QueueLength
kafka.server:type=ForwardingManager,name=RemoteTimeMs.p99
kafka.server:type=ForwardingManager,name=RemoteTimeMs.p999

Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-08-28 11:33:31 -07:00
José Armando García Sancio 145fa49e54 KAFKA-17426; Check node directory id for KRaft (#17017)
Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-08-28 11:33:09 -07:00
Kirk True a87b501a47 KAFKA-17335 Lack of default for URL encoding configuration for OAuth causes NPE (#16990)
AccessTokenRetrieverFactory uses the value of sasl.oauthbearer.header.urlencode provided by the user, or null if no value was provided for that configuration. When the HttpAccessTokenRetriever is created the JVM attempts to unbox the value into a boolean, a NullPointerException is thrown.

The fix is to explicitly check the Boolean, and if it's null, use Boolean.FALSE.

Reviewers: bachmanity1 <81428651+bachmanity1@users.noreply.github.com>, Chia-Ping Tsai <chia7712@gmail.com>
2024-08-28 23:12:09 +08:00
Arpit Goyal a19792fbd7 KAFKA-17422: Adding copySegmentLatch countdown after expiration task is over (#17012)
The given test took 5 seconds as the logic was waiting completely for 5 seconds for the expiration task to be completed. Adding copySegmentLatch countdown after expiration task is over

Reviewers: Luke Chen <showuon@gmail.com>, Chia-Ping Tsai <chia7712@gmail.com>
2024-08-28 10:45:51 +08:00
Kuan-Po Tseng 6d2b81e07f KAFKA-17360 local log retention ms/bytes "-2" is not treated correctly (#16932)
1) When the local.retention.ms/bytes is set to -2, we didn't replace it with the server-side retention.ms/bytes config, so the -2 local retention won't take effect.
2) When setting retention.ms/bytes to -2, we can notice this log message:

```
Deleting segment LogSegment(baseOffset=10045, size=1037087, lastModifiedTime=1724040653922, largestRecordTimestamp=1724040653835) due to local log retention size -2 breach. Local log size after deletion will be 13435280. (kafka.log.UnifiedLog) [kafka-scheduler-6]
```
This is not helpful for users. We should replace -2 with real retention value when logging.

Reviewers: Luke Chen <showuon@gmail.com>, Chia-Ping Tsai <chia7712@gmail.com>
2024-08-25 19:46:43 +08:00
TengYao Chi db686fb964 KAFKA-17331 Set correct version for EarliestLocalSpec and LatestTieredSpec (#16876)
Add the version check to client side when building ListOffsetRequest for the specific timestamp:
1) the version must be >=8 if timestamp=-4L (EARLIEST_LOCAL_TIMESTAMP)
2) the version must be >=9 if timestamp=-5L (LATEST_TIERED_TIMESTAMP)

Reviewers: PoAn Yang <payang@apache.org>, Chia-Ping Tsai <chia7712@gmail.com>
2024-08-25 17:40:26 +08:00
TengYao Chi dcb4578903 KAFKA-17315 Fix the behavior of delegation tokens that expire immediately upon creation in KRaft mode (#16858)
In kraft mode, expiring delegation token (`expiryTimePeriodMs` < 0) has following different behavior to zk mode.

1. `ExpiryTimestampMs` is set to "expiryTimePeriodMs" [0] rather than "now" [1]
2. it throws exception directly if the token is expired already [2]. By contrast, zk mode does not. [3]

[0] 49fc14f611/metadata/src/main/java/org/apache/kafka/controller/DelegationTokenControlManager.java (L316)
[1] 49fc14f611/core/src/main/scala/kafka/server/DelegationTokenManagerZk.scala (L292)
[2] 49fc14f611/metadata/src/main/java/org/apache/kafka/controller/DelegationTokenControlManager.java (L305)
[3] 49fc14f611/core/src/main/scala/kafka/server/DelegationTokenManagerZk.scala (L293)

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-08-25 07:30:34 +08:00
Matthias J. Sax 1f6d5aec82 MINOR: fix HTML for topology.optimization config (#16953)
The HTML rendering broke via https://issues.apache.org/jira/browse/KAFKA-14209 in 3.4 release. The currently shown value is some garbage org.apache.kafka.streams.StreamsConfig$$Lambda$20/0x0000000800c0cf18@b1bc7ed

cf https://kafka.apache.org/documentation/#streamsconfigs_topology.optimization

Verified the fix via running StreamsConfig#main() locally.

Reviewers: Bill Bejeck <bill@confluent.io>, Chia-Ping Tsai <chia7712@gmail.com>
2024-08-22 17:14:13 -07:00
Ken Huang 29bbb6555c KAFKA-17336 Add IT to make sure the production MV does not use unstable version of LIST_OFFSET (#16893)
- due to the server config UNSTABLE_API_VERSIONS_ENABLE_CONFIG is true, so we can't test the scenario of ListOffsetsRequest is unstable version. We want to test this case in this PR
- get the MV from metadataCache.metadataVersion() instead of config.interBrokerProtocolVersion since MV can be set dynamically.

Reviewers: Jun Rao <junrao@gmail.com>, Chia-Ping Tsai <chia7712@gmail.com>
2024-08-23 03:48:45 +08:00
Alyssa Huang b377ea94c5 KAFKA-17305; Check broker registrations for missing features (#16848)
When a broker tries to register with the controller quorum, its registration should be rejected if it doesn't support a feature that is currently enabled. (A feature is enabled if it is set to a non-zero feature level.) This is important for the newly added kraft.version feature flag.

Reviewers: Colin P. McCabe <cmccabe@apache.org>, José Armando García Sancio <jsancio@apache.org>
2024-08-21 11:15:17 -07:00
José Armando García Sancio 283b82d46a KAFKA-17333; ResignedState should not notify of leader change (#16900)
When a voter fails as leader (LeaderState) the quorum-state still states that it is the leader of
the epoch. When the voter starts it never starts as leader and instead starts as resigned
(ResignedState) if it was previously a leader. This causes the KRaft client to immediately notify
the state machine (e.g QuorumController) that it is leader or active. This is incorrect for two
reasons.

One, the controller cannot be notified of leadership until it has reached the LEO. If the
controller is notified before that it will generate and append records that are not based on the
latest state.

Two, it is not practical to notify of local leadership when it is resigned since any write
operation (prepareAppend and schedulePreparedAppend) will fail with NotLeaderException while KRaft
is in the resigned state.

Reviewers: Colin P. McCabe <cmccabe@apache.org>, David Arthur <mumrah@gmail.com>
2024-08-21 09:32:09 -07:00
Sean Quah 321ab71192
KAFKA-17279: Handle retriable errors from offset fetches (#16826) (#16934)
Handle retriable errors from offset fetches in ConsumerCoordinator.

Reviewers: Lianet Magrans <lianetmr@gmail.com>, David Jacot <djacot@confluent.io>
2024-08-21 05:17:31 -07:00
Mason Chen b7a97e7102 KAFKA-17169: Add EndpointsTest (#16659)
Reviewers: Omnia Ibrahim <o.g.h.ibrahim@gmail.com>, Colin P. McCabe <cmccabe@apache.org>
2024-08-20 15:09:27 -07:00
José Armando García Sancio 313af4e83d KAFKA-17332; Controller always flush and can call resign on observers (#16907)
This change includes two improvements.

When the leader removes itself from the voters set clients of RaftClient may call resign. In those cases the leader is not in the voter set and should not throw an exception.

Controllers that are observers must flush the log on every append because leader may be trying to add them to the voter set. Leader always assume that voters flush their disk before sending a Fetch request.

Reviewers: David Arthur <mumrah@gmail.com>, Alyssa Huang <ahuang@confluent.io>
2024-08-20 00:45:01 +00:00
José Armando García Sancio ed7cadd4c0 KAFKA-16842; Fix config validation and support unknown voters (#16892)
This change fixes the Kafka configuration validation to take into account the reconfiguration changes to configuration and allows KRaft observers to start with an unknown set of voters.

For the Kafka configuration validation the high-level change is that now the user only needs to specify either the controller.quorum.bootstrap.servers property or the controller.quorum.voters property. The other notable change in the configuration is that controller listeners can now be (and should be) specified in advertise.listeners property.

Because Kafka can now be configured without any voters and just the bootstrap servers. The KRaft client needs to allow for an unknown set of voters during the initial startup. This is done by adding the VoterSet#empty set of voters to the KRaftControlRecordStateMachine.

Lastly the RaftClientTestContext type is updated to support this new configuration for KRaft and a test is added to verify that observers can start and send Fetch requests when the voters are unknown.

Reviewers: David Arthur <mumrah@gmail.com>
2024-08-16 19:55:16 +00:00
TengYao Chi bcf4c73bae KAFKA-17238 Move VoterSet and ReplicaKey from raft.internals to raft (#16775)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-08-16 00:45:32 +08:00
Josep Prat 3b90bbaf6f MINOR: Fix visibility for classes exposed outside of their scope (#16886)
These 2 classes are package protected but they are part of the public
API of public methods. To have clean APIs we should make this
consistent.

Static class ReplicaState is exposed in RaftUtil#singletonDescribeQuorumResponse method which is public.

RequestSender is implemented by a public class and it's exposed in the public constructor of AddVoterHandler.

Reviewers: José Armando García Sancio <jsancio@apache.org>
2024-08-15 16:11:07 +00:00
Ken Huang ba1995704a KAFKA-17326 The LIST_OFFSET request is removed from the "Api Keys" page (#16870)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-08-15 19:00:14 +08:00
José Armando García Sancio 4ea3b6181a KAFKA-17304; Make RaftClient API for writing to log explicit (#16862)
RaftClient API is changed to separate the batch accumulation (RaftClient#prepareAppend) from scheduling the append of accumulated batches (RaftClient#schedulePrepatedAppend) to the KRaft log. This change is needed to better match the controller's flow of replaying the generated records before replicating them. When the controller replay records it needs to know the offset associated with the record. To compute a table offset the KafkaClient needs to be aware of the records and their log position.

The controller uses this new API by generated the cluster metadata records, compute their offset using RaftClient#prepareAppend, replay the records in the state machine, and finally allowing KRaft to append the records with RaftClient#schedulePreparedAppend.

To implement this API the BatchAccumulator is changed to also support this access pattern. This is done by adding a drainOffset to the implementation. The batch accumulator is allowed to return any record and batch that is less than the drain offset.

Lastly, this change also removes some functionality that is no longer needed like non-atomic appends and validation of the base offset.

Reviewers: Colin Patrick McCabe <cmccabe@apache.org>, David Arthur <mumrah@gmail.com>
2024-08-14 19:48:47 +00:00
PoAn Yang 41107041f3 KAFKA-17309 Fix flaky testCallFailWithUnsupportedVersionExceptionDoesNotHaveConcurrentModificationException (#16854)
Reviewers: TengYao Chi <kitingiao@gmail.com>, Chia-Ping Tsai <chia7712@gmail.com>
2024-08-13 21:06:58 +08:00
Ken Huang 32346c646b KAFKA-17319 change ListOffsetsRequest latestVersionUnstable to false (#16865)
Reviewers: Luke Chen <showuon@gmail.com>, PoAn Yang <payang@apache.org>, TengYao Chi <kitingiao@gmail.com>, Chia-Ping Tsai <chia7712@gmail.com>
2024-08-13 20:45:01 +08:00
Luke Chen 500dc27c2b KAFKA-17300: Add document for new tiered storage feature in v3.9.0. (#16836)
- Added document for disabling tiered storage at topic level
- Added notable changes items in v3.9.0 for tiered storage quota

Reviewers: Satish Duggana <satishd@apache.org>, Kamal Chandraprakash<kamal.chandraprakash@gmail.com>, Abhijeet Kumar<abhijeet.cse.kgp@gmail.com>
2024-08-13 17:33:12 +05:30
Nancy df5c31e8df Kafka 16887 : Upgrade the document to add remote copy/fetch quotas metrics values. (#16863)
Reviewers: Abhijeet Kumar<abhijeet.cse.kgp@gmail.com>, Luke Chen <showuon@gmail.com>, Satish Duggana <satishd@apache.org>
2024-08-13 12:31:52 +05:30
Colin Patrick McCabe b53dfebad5 KAFKA-17018: update MetadataVersion for the Kafka release 3.9 (#16841)
- Mark 3.9-IV0 as stable. Metadata version 3.9-IV0 should return Fetch version 17.

- Move ELR to 4.0-IV0. Remove 3.9-IV1 since it's no longer needed.

- Create a new 4.0-IV1 MV for KIP-848.

Reviewers: Jun Rao <junrao@gmail.com>, Chia-Ping Tsai <chia7712@gmail.com>, Justine Olshan <jolshan@confluent.io>
2024-08-12 16:31:47 -07:00
Colin Patrick McCabe 161c8c6383 KAFKA-17190: AssignmentsManager gets stuck retrying on deleted topics (#16672)
In MetadataVersion 3.7-IV2 and above, the broker's AssignmentsManager sends an RPC to the
controller informing it about which directory we have chosen to place each new replica on.
Unfortunately, the code does not check to see if the topic still exists in the MetadataImage before
sending the RPC. It will also retry infinitely. Therefore, after a topic is created and deleted in
rapid succession, we can get stuck including the now-defunct replica in our subsequent
AssignReplicasToDirsRequests forever.

In order to prevent this problem, the AssignmentsManager should check if a topic still exists (and
is still present on the broker in question) before sending the RPC. In order to prevent log spam,
we should not log any error messages until several minutes have gone past without success.
Finally, rather than creating a new EventQueue event for each assignment request, we should simply
modify a shared data structure and schedule a deferred event to send the accumulated RPCs. This
will improve efficiency.

Reviewers: Igor Soarez <i@soarez.me>, Ron Dagostino <rndgstn@gmail.com>
2024-08-12 12:24:04 -07:00
Kuan-Po Tseng fccae40564 KAFKA-17310 locking the offline dir can destroy the broker exceptionally (#16856)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-08-12 16:45:09 +08:00
Alyssa Huang 5c97ab0a34 KAFKA-17067; Fix KRaft transition to CandidateState (#16820)
Only voters should be able to transition to Candidate state. This removes VotedState as one of the EpochStates and moves voted information into UnattachedState.

Reviewers: José Armando García Sancio <jsancio@apache.org>
2024-08-10 11:45:04 +00:00
José Armando García Sancio 682299a7df KAFKA-16534; Implemeent update voter sending (#16837)
This change implements the KRaft voter sending UpdateVoter request. The
UpdateVoter RPC is used to update a voter's listeners and supported
kraft versions. The UpdateVoter RPC is sent if the replicated voter set
(VotersRecord in the log) doesn't match the local voter's supported
kraft versions and controller listeners.

To not starve the Fetch request, the UpdateVoter request is sent at most
every 3 fetch timeouts. This is required to make sure that replication
is making progress and eventually the voter set in the replicated log
matches the local voter configuration.

This change also modifies the semantic for UpdateVoter. Now the
UpdateVoter response is sent right after the leader has created the new
voter set. This is required so that updating voter can transition from
sending UpdateVoter request to sending Fetch request. If the leader
waits for the VotersRecord control record to commit before sending the
UpdateVoter response, it may never send the UpdateVoter response. This
can happen if the leader needs that voter's Fetch request to commit the
control record.

Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-08-09 00:05:18 +00:00
Colin Patrick McCabe 75cf36050d KAFKA-16523; kafka-metadata-quorum: support add-controller and remove-controller (#16774)
This PR adds support for add-controller and remove-controller in the kafka-metadata-quorum.sh
command-line tool. It also fixes some minor server-side bugs that blocked the tool from working.

In kafka-metadata-quorum.sh, the implementation of remove-controller is fairly straightforward. It
just takes some command-line flags and uses them to invoke AdminClient. The add-controller
implementation is a bit more complex because we have to look at the new controller's configuration
file. The parsing logic for the advertised.listeners and listeners server configurations that we
need was previously implemented in the :core module. However, the gradle module where
kafka-metadata-quorum.sh lives, :tools, cannot depend on :core. Therefore, I moved listener parsing
into SocketServerConfigs.listenerListToEndPoints. This will be a small step forward in our efforts
to move Kafka configuration out of :core.

I also made some minor changes in kafka-metadata-quorum.sh and Kafka-storage-tool.sh to handle
--help without displaying a backtrace on the screen, and give slightly better error messages on
stderr. Also, in DynamicVoter.toString, we now enclose the host in brackets if it contains a colon
(as IPV6 addresses can).

This PR fixes our handling of clusterId in addRaftVoter and removeRaftVoter, in two ways. Firstly,
it marks clusterId as nullable in the AddRaftVoterRequest.json and RemoveRaftVoterRequest.json
schemas, as it was always intended to be. Secondly, it allows AdminClient to optionally send
clusterId, by using AddRaftVoterOptions and RemoveRaftVoterOptions. We now also remember to
properly set timeoutMs in AddRaftVoterRequest. This PR adds unit tests for
KafkaAdminClient#addRaftVoter and KafkaAdminClient#removeRaftVoter, to make sure they are sending
the right things.

Finally, I fixed some minor server-side bugs that were blocking the handling of these RPCs.
Firstly, ApiKeys.ADD_RAFT_VOTER and ApiKeys.REMOVE_RAFT_VOTER are now marked as forwardable so that
forwarding from the broker to the active controller works correctly. Secondly,
org.apache.kafka.raft.KafkaNetworkChannel has now been updated to enable API_VERSIONS_REQUEST and
API_VERSIONS_RESPONSE.

Co-authored-by: Murali Basani muralidhar.basani@aiven.io
Reviewers: José Armando García Sancio <jsancio@apache.org>, Alyssa Huang <ahuang@confluent.io>
2024-08-09 00:03:13 +00:00
PoAn Yang c4cc6d2ff3 KAFKA-17223 Retrying the call after encoutering UnsupportedVersionException will cause ConcurrentModificationException (#16753)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-08-08 16:09:34 -07:00
Alyssa Huang b048798a09 KAFKA-16521: Have Raft endpoints printed as name://host:port (#16830)
Reviewers: Colin P. McCabe <cmccabe@apache.org>
2024-08-08 09:23:03 -07:00
Dmitry Werner 9230a3899f
KAFKA-17242: Do not log spurious timeout message for MirrorCheckpointTask sync store startup (#16773)
Reviewers: Chris Egerton <chrise@aiven.io>
2024-08-08 10:04:00 -04:00
TengYao Chi 0b57b36c8f
KAFKA-17232: Do not generate task configs in MirrorCheckpointConnector if initial consumer group load times out (#16767)
Reviewers: Hongten <hongtenzone@foxmail.com>, Chris Egerton <chrise@aiven.io>
2024-08-08 09:58:34 -04:00
Luke Chen 7fe3cec4eb KAFKA-17236: Handle local log deletion when remote.log.copy.disabled=true (#16765)
Handle local log deletion when remote.log.copy.disabled=true based on the KIP-950.

When tiered storage is disabled or becomes read-only on a topic, the local retention configuration becomes irrelevant, and all data expiration follows the topic-wide retention configuration exclusively.

- added remoteLogEnabledAndRemoteCopyEnabled method to check if this topic enables tiered storage and remote log copy is enabled. We should adopt local.retention.ms/bytes when remote.storage.enable=true,remote.log.copy.disable=false.
- Changed to use retention.bytes/retention.ms when remote copy disabled.
- Added validation to ask users to set local.retention.ms == retention.ms and local.retention.bytes == retention.bytes
- Added tests

Reviewers: Kamal Chandraprakash<kamal.chandraprakash@gmail.com>, Satish Duggana <satishd@apache.org>, Christo Lolov <lolovc@amazon.com>
2024-08-08 19:49:23 +08:00
Ken Huang dd5e7a8291 KAFKA-17276; replicaDirectoryId for Fetch and FetchSnapshot should be ignorable (#16819)
The replicaDirectoryId field for FetchRequest and FetchSnapshotRequest should be ignorable. This allows data objects with the directory id to be serialized to any version of the requests.

Reviewers: José Armando García Sancio <jsancio@apache.org>, Chia-Ping Tsai <chia7712@apache.org>
2024-08-08 00:58:01 +00:00
dujian0068 c736d02b52 KAFKA-16584: Make log processing summary configurable or debug--update upgrade-guide (#16709)
Updates Kafka Streams upgrade-guide for KIP-1049.

Reviewers: Bill Bejeck <bill@confluent.io>, Matthias J. Sax <matthias@confluent.io>
2024-08-06 12:09:56 -07:00
Mickael Maison 4e6508b5e3 KAFKA-17227: Refactor compression code to only load codecs when used (#16782)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, Josep Prat <josep.prat@aiven.io>
2024-08-06 11:04:28 +02:00
Kuan-Po Tseng 4537c8af5b KAFKA-17235 system test test_performance_service.py failed (#16789)
related to https://issues.apache.org/jira/browse/KAFKA-17235

The root cause of this issue is a change we introduced in KAFKA-16879, where we modified the PushHttpMetricsReporter constructor to use Time.System [1]. However, Time.System doesn't exist in Kafka versions 0.8.2 and 0.9.

In test_performance_services.py, we have system tests for Kafka versions 0.8.2 and 0.9 [2]. These tests always use the tools JAR from the trunk branch, regardless of the Kafka version being tested [3], while the client JAR aligns with the Kafka version specified in the test suite [4]. This discrepancy is what causes the issue to arise.

To resolve this issue, we have a few options:

1) Add Time.System to Kafka 0.8.2 and 0.9: This isn't practical, as we no longer maintain these versions.
2) Modify the PushHttpMetricsReporter constructor to use new SystemTime() instead of Time.System: This would contradict the intent of KAFKA-16879, which aims to make SystemTime a singleton.
3) Implement Time in PushHttpMetricsReporter use the time to get current time
4) Remove system tests for Kafka 0.8.2 and 0.9 from test_performance_services.py

Given that we no longer maintain Kafka 0.8.2 and 0.9, and altering the constructor goes against the design goals of KAFKA-16879, option 4 appears to be the most feasible solution. However, I'm not sure whether it's acceptable to remove these old version tests. Maybe someone else has a better solution

"We'll proceed with option 3 since support for versions 0.8 and 0.9 is still required, meaning we can't remove those Kafka versions from the system tests."

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-08-06 14:52:17 +08:00
José Armando García Sancio 81edb74c5e KAFKA-16533; Update voter handling
Add support for handling the update voter RPC. The update voter RPC is used to automatically update
the voters supported kraft versions and available endpoints as the operator upgrades and
reconfigures the KRaft controllers.

The add voter RPC is handled as follow:

1. Check that the leader has fenced the previous leader(s) by checking that the HWM is known;
   otherwise, return the REQUEST_TIMED_OUT error.

2. Check that the cluster supports kraft.version 1; otherwise, return the UNSUPPORTED_VERSION error.

3. Check that there are no uncommitted voter changes, otherwise return the REQUEST_TIMED_OUT error.

4. Check that the updated voter still supports the currently finalized kraft.version; otherwise
   return the INVALID_REQUEST error.

5. Check that the updated voter is still listening on the default listener.

6. Append the updated VotersRecord to the log. The KRaft internal listener will read this
   uncommitted record from the log and update the voter in the set of voters.

7. Wait for the VotersRecord to commit using the majority of the voters. Return a REQUEST_TIMED_OUT
   error if it doesn't commit in time.

8. Send the UpdateVoter successful response to the voter.

This change also implements the ability for the leader to update its own entry in the voter
set when it becomes leader for an epoch. This is done by updating the voter set and writing a
control batch as the first batch in a new leader epoch.

Finally, fix a bug in KafkaAdminClient's handling of removeRaftVoterResponse where we tried to cast
the response to the wrong type.

Reviewers: Alyssa Huang <ahuang@confluent.io>, Colin P. McCabe <cmccabe@apache.org>
2024-08-05 13:31:51 -07:00
Colin Patrick McCabe 129e7fb0b8 KAFKA-16518: Implement KIP-853 flags for storage-tool.sh (#16669)
As part of KIP-853, storage-tool.sh now has two new flags: --standalone, and --initial-voters. This PR implements these two flags in storage-tool.sh.

There are currently two valid ways to format a cluster:

The pre-KIP-853 way, where you use a statically configured controller quorum. In this case, neither --standalone nor --initial-voters may be specified, and kraft.version must be set to 0.

The KIP-853 way, where one of --standalone and --initial-voters must be specified with the initial value of the dynamic controller quorum. In this case, kraft.version must be set to 1.

This PR moves the formatting logic out of StorageTool.scala and into Formatter.java. The tool file was never intended to get so huge, or to implement complex logic like generating metadata records. Those things should be done by code in the metadata or raft gradle modules. This is also useful for junit tests, which often need to do formatting. (The 'info' and 'random-uuid' commands remain in StorageTool.scala, for now.)

Reviewers: José Armando García Sancio <jsancio@apache.org>
2024-08-05 13:31:40 -07:00
Josep Prat c7d02127b1
KAFKA-17227: Update zstd-jni lib (#16763)
* KAFKA-17227: Update zstd-jni lib
* Add note in upgrade docs
* Change zstd-jni version in docker native file and add warning in dependencies.gradle file
* Add reference to snappy in upgrade

Reviewers:  Chia-Ping Tsai <chia7712@gmail.com>,  Mickael Maison <mickael.maison@gmail.com>
2024-08-05 09:55:42 +02:00
Kuan-Po Tseng b65644c3e3 KAFKA-16154: Broker returns offset for LATEST_TIERED_TIMESTAMP (#16783)
This pr support EarliestLocalSpec LatestTierSpec in GetOffsetShell, and add integration tests.

Reviewers: Luke Chen <showuon@gmail.com>, Chia-Ping Tsai <chia7712@gmail.com>, PoAn Yang <payang@apache.org>
2024-08-05 10:41:56 +08:00
Matthias J. Sax 2ddbfebecb KAFKA-16448: Unify error-callback exception handling (#16745)
Follow up code cleanup for KIP-1033.

This PR unifies the handling of both error cases for exception handlers:
 - handler throws an exception
 - handler returns null

The unification happens for all 5 handler cases:
 - deserialzation
 - production / serialization
 - production / send
 - processing
 - punctuation

Reviewers:  Sebastien Viale <sebastien.viale@michelin.com>, Loic Greffier <loic.greffier@michelin.com>, Bill Bejeck <bill@confluent.io>
2024-08-03 13:08:11 -07:00
Luke Chen b622121c0a KAFKA-16855: remote log disable policy in KRaft (#16653)
Reviewers: Kamal Chandraprakash <kamal.chandraprakash@gmail.com>, Christo Lolov <lolovc@amazon.com>
2024-08-03 20:21:05 +08:00
Luke Chen 38db4c46ff KAFKA-17205: Allow topic config validation in controller level in KRaft mode (#16693)
Reviewers: Kamal Chandraprakash <kamal.chandraprakash@gmail.com>, Christo Lolov <lolovc@amazon.com>
2024-08-03 20:20:19 +08:00
PoAn Yang 66485b04c6 KAFKA-16480: ListOffsets change should have an associated API/IBP version update (#16781)
1. Use oldestAllowedVersion as 9 if using ListOffsetsRequest#EARLIEST_LOCAL_TIMESTAMP or ListOffsetsRequest#LATEST_TIERED_TIMESTAMP.
   2. Add test cases to ListOffsetsRequestTest#testListOffsetsRequestOldestVersion to make sure requireTieredStorageTimestamp return 9 as minVersion.
   3. Add EarliestLocalSpec and LatestTierSpec to OffsetSpec.
   4. Add more cases to KafkaAdminClient#getOffsetFromSpec.
   5. Add testListOffsetsEarliestLocalSpecMinVersion and testListOffsetsLatestTierSpecSpecMinVersion to KafkaAdminClientTest to make sure request builder has oldestAllowedVersion as 9.

Signed-off-by: PoAn Yang <payang@apache.org>

Reviewers: Luke Chen <showuon@gmail.com>
2024-08-03 20:17:58 +08:00
TengYao Chi 4e75c57bbb KAFKA-17245: Revert TopicRecord changes. (#16780)
Revert KAFKA-16257 changes because KIP-950 doesn't need it anymore.

Reviewers: Luke Chen <showuon@gmail.com>
2024-08-03 20:17:25 +08:00
TengYao Chi 6b039ce75b KAFKA-16390: add `group.coordinator.rebalance.protocols=classic,consumer` to broker configs when system tests need the new coordinator (#16715)
Fix an issue that cause system test failing when using AsyncKafkaConsumer.
A configuration option, group.coordinator.rebalance.protocols, was introduced to specify the rebalance protocols used by the group coordinator. By default, the rebalance protocol is set to classic. When the new group coordinator is enabled, the rebalance protocols are set to classic,consumer.

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>, David Jacot <djacot@confluent.io>, Lianet Magrans <lianetmr@gmail.com>, Kirk True <kirk@kirktrue.pro>, Justine Olshan <jolshan@confluent.io>
2024-08-02 16:19:04 -07:00
Sebastien Viale 4afe5f380a KAFKA-16448: Update documentation (#16776)
Updated docs for KIP-1033.

Reviewers: Matthias J. Sax <matthias@confluent.io>
2024-08-02 09:54:51 -07:00
Ken Huang fbb598ce82 KAFKA-16666 Migrate GroupMetadataMessageFormatter` to tools module (#16748)
we need to migate GroupMetadataMessageFormatter from scala code to java code,and make the message format is json pattern

Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-08-02 11:54:43 +08:00
Kondrat Bertalan 60e1478fb9
KAFKA-17192 Fix MirrorMaker2 worker config does not pass config.provi… (#16678)
Reviewers: Chris Egerton <chrise@aiven.io>
2024-08-01 16:13:38 -04:00
Alyssa Huang 25f04804cd KAFKA-16521; kafka-metadata-quorum describe command changes for KIP-853 (#16759)
describe --status now includes directory id and endpoint information for voter and observers.
describe --replication now includes directory id.

Reviewers: Colin P. McCabe <cmccabe@apache.org>, José Armando García Sancio <jsancio@apache.org>
2024-08-01 19:30:56 +00:00
Sebastien Viale 578fef2355 KAFKA-16448: Handle processing exceptions in punctuate (#16300)
This PR is part of KIP-1033 which aims to bring a ProcessingExceptionHandler to Kafka Streams in order to deal with exceptions that occur during processing.

This PR actually catches processing exceptions from punctuate.

Co-authored-by: Dabz <d.gasparina@gmail.com>
Co-authored-by: loicgreffier <loic.greffier@michelin.com>

Reviewers: Bruno Cadonna <bruno@confluent.io>, Matthias J. Sax <matthias@confluent.io>
2024-07-31 16:06:39 -07:00
Matthias J. Sax 2c957a6e5c MINOR: simplify code which calles `Punctuator.punctuate()` (#16725)
Reviewers: Bill Bejeck <bill@confluent.io>
2024-07-31 16:06:25 -07:00
Loïc GREFFIER aaed1bdd89 KAFKA-16448: Unify class cast exception handling for both key and value (#16736)
Part of KIP-1033. Minor code cleanup.

Reviewers: Matthias J. Sax <matthias@confluent.io>
2024-07-31 13:23:03 -07:00
Matthias J. Sax ccb04acb56
Revert "KAFKA-16508: Streams custom handler should handle the timeout exceptions (#16450)" (#16738)
This reverts commit 15a4501bde.

We consider this change backward incompatible and will fix forward for 4.0
release via KIP-1065, but need to revert for 3.9 release.

Reviewers: Josep Prat <josep.prat@aiven.io>, Bill Bejeck <bill@confluent.io>
2024-07-31 10:29:02 -07:00
Ken Huang fbdfd0d596 KAFKA-16666 Migrate OffsetMessageFormatter to tools module (#16689)
Reviewers: Chia-Ping Tsai <chia7712@gmail.com>
2024-07-31 15:19:28 +08:00
Sebastien Viale c8dc09c265 KAFKA-16448: Handle fatal user exception during processing error (#16675)
This PR is part of KIP-1033 which aims to bring a ProcessingExceptionHandler to Kafka Streams in order to deal with exceptions that occur during processing.

This PR catch the exceptions thrown while handling a processing exception

Co-authored-by: Dabz <d.gasparina@gmail.com>
Co-authored-by: loicgreffier <loic.greffier@michelin.com>

Reviewers: Bruno Cadonna <bruno@confluent.io>, Matthias J. Sax <matthias@confluent.io>
2024-07-30 22:57:31 -07:00
Josep Prat 0370a6464b
MINOR: Add text and link to blog in announcement template email (#16734)
Reviewers: Igor Soarez <soarez@apple.com>
2024-07-30 21:50:31 +02:00
Josep Prat 3d2ea547d8
KAFKA-17214: Add 3.8.0 version to core and client system tests (#16726)
Reviewers: Greg Harris <greg.harris@aiven.io>
2024-07-30 19:42:12 +02:00
Josep Prat b8c54c3f38
KAFKA-17214: Add 3.8.0 version to streams system tests (#16728)
* KAFKA-17214: Add 3.8.0 version to streams system tests

Reviewers: Bill Bejeck <bbejeck@gmail.com>
2024-07-30 19:41:36 +02:00
PaulRMellor 0969789973 KAFKA-15469: Add documentation for configuration providers (#16650)
Reviewers: Mickael Maison <mickael.maison@gmail.com>
2024-07-30 15:35:40 +02:00
Josep Prat bc243ab1e8
MINOR: Add 3.8.0 to system tests (#16714)
Reviewers:  Manikumar Reddy <manikumar.reddy@gmail.com>
2024-07-30 09:20:35 +02:00
Matthias J. Sax b8532070f7 HOTFIX: fix compilation error 2024-07-29 21:08:49 -07:00
Sebastien Viale 10d9f7872d KAFKA-16448: Add ErrorHandlerContext in deserialization exception handler (#16432)
This PR is part of KIP1033 which aims to bring a ProcessingExceptionHandler to Kafka Streams in order to deal with exceptions that occur during processing.

This PR expose the new ErrorHandlerContext as a parameter to the Deserialization exception handlers and deprecate the previous handle signature.

Co-authored-by: Dabz <d.gasparina@gmail.com>
Co-authored-by: loicgreffier <loic.greffier@michelin.com>

Reviewers: Bruno Cadonna <bruno@confluent.io>, Matthias J. Sax <matthias@confluent.io>
2024-07-29 20:35:25 -07:00
Sebastien Viale a4ea9aec73 KAFKA-16448: Add ErrorHandlerContext in production exception handler (#16433)
This PR is part of KIP-1033 which aims to bring a ProcessingExceptionHandler to Kafka Streams in order to deal with exceptions that occur during processing.

This PR expose the new ErrorHandlerContext as a parameter to the Production exception handler and deprecate the previous handle signature.

Co-authored-by: Dabz <d.gasparina@gmail.com>
Co-authored-by: loicgreffier <loic.greffier@michelin.com>

Reviewers: Bruno Cadonna <bruno@confluent.io>, Matthias J. Sax <matthias@confluent.io>
2024-07-29 20:35:17 -07:00
Colin P. McCabe f26f0b6626 tests/kafkatest/version.py: Add 3.9.0 as DEV_VERSION 2024-07-29 15:58:04 -07:00
1555 changed files with 94201 additions and 21044 deletions

2
.github/CODEOWNERS vendored
View File

@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
* @superhx @SCNieh @ShadowySpirits @Chillax-0v0
* @superhx @Gezi-lzq @1sonofqiu @woshigaopp

View File

@ -49,7 +49,7 @@ jobs:
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.9.0
- name: Checkstyle
run: ./gradlew --build-cache rat checkstyleMain checkstyleTest
run: ./gradlew --build-cache rat checkstyleMain checkstyleTest spotlessJavaCheck
spotbugs:
name: "Spotbugs"
runs-on: ${{ matrix.os }}

View File

@ -0,0 +1,67 @@
name: Docker Bitnami Release
on:
workflow_dispatch:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+-rc[0-9]+'
jobs:
docker-release:
name: Docker Image Release
strategy:
matrix:
platform: [ "ubuntu-24.04" ]
jdk: ["17"]
runs-on: ${{ matrix.platform }}
permissions:
contents: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.jdk }}
distribution: "zulu"
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.12.0
- name: Get project version
id: get_project_version
run: |
project_version=$(./gradlew properties | grep "version:" | awk '{print $2}')
echo "PROJECT_VERSION=${project_version}" >> $GITHUB_OUTPUT
- name: Build TarGz
run: |
./gradlew -Pprefix=automq-${{ github.ref_name }}_ --build-cache --refresh-dependencies clean releaseTarGz
# docker image release
- name: Cp TarGz to Docker Path
run: |
cp ./core/build/distributions/automq-${{ github.ref_name }}_kafka-${{ steps.get_project_version.outputs.PROJECT_VERSION }}.tgz ./container/bitnami
- name: Determine Image Tags
id: image_tags
run: |
echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/automq:${{ github.ref_name }}-bitnami" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_READ_WRITE_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: ./container/bitnami
push: true
tags: ${{ steps.image_tags.outputs.tags }}
platforms: linux/amd64,linux/arm64

View File

@ -0,0 +1,70 @@
name: AutoMQ Kafka Docker Release
on:
workflow_dispatch:
inputs:
tag:
description: 'AutoMQ Version Tag'
required: false
type: string
workflow_run:
workflows: ["GitHub Release"]
types:
- completed
env:
KAFKA_VERSION: "3.9.0"
jobs:
automq-kafka-release:
name: AutoMQ Kafka Docker Image Release
strategy:
matrix:
platform: [ "ubuntu-24.04" ]
jdk: [ "17" ]
runs-on: ${{ matrix.platform }}
permissions:
contents: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Get release tag
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ github.event.inputs.tag }}" ]]; then
TAG="${{ github.event.inputs.tag }}"
# use the latest tag if not specified
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
TAG=$(git ls-remote --tags https://github.com/AutoMQ/automq.git | grep -v '\^{}' | tail -1 | sed 's/.*refs\/tags\///')
else
TAG="${{ github.event.workflow_run.head_branch }}"
fi
AUTOMQ_URL="https://github.com/AutoMQ/automq/releases/download/${TAG}/automq-${TAG}_kafka-${KAFKA_VERSION}.tgz"
{
echo "AUTOMQ_VERSION=${TAG}-kafka"
echo "AUTOMQ_URL=${AUTOMQ_URL}"
} >> $GITHUB_ENV
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_READ_WRITE_TOKEN }}
- name: Build and Push Docker Image
run: |
python3 -m venv .venv
source .venv/bin/activate
.venv/bin/pip install setuptools
cd docker
python3 docker_release.py \
${{ secrets.DOCKERHUB_USERNAME }}/automq:${AUTOMQ_VERSION} \
--kafka-url ${AUTOMQ_URL}

View File

@ -1,6 +1,7 @@
name: Docker Release
on:
workflow_dispatch:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
@ -12,7 +13,7 @@ jobs:
name: Docker Image Release
strategy:
matrix:
platform: [ "ubuntu-22.04" ]
platform: [ "ubuntu-24.04" ]
jdk: ["17"]
runs-on: ${{ matrix.platform }}
permissions:
@ -69,4 +70,4 @@ jobs:
context: ./docker
push: true
tags: ${{ steps.image_tags.outputs.tags }}
platforms: linux/amd64,linux/arm64
platforms: linux/amd64,linux/arm64

View File

@ -0,0 +1,84 @@
name: Docker Strimzi Release
on:
workflow_dispatch:
inputs:
tag:
description: 'AutoMQ Version Tag'
required: false
type: string
workflow_run:
workflows: ["GitHub Release"]
types:
- completed
env:
KAFKA_VERSION: "3.9.0"
STRIMZI_REPO: "https://github.com/AutoMQ/strimzi-kafka-operator.git"
STRIMZI_BRANCH: "main"
jobs:
strimzi-release:
name: Strimzi Image Release
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
strategy:
matrix:
platform: [ "ubuntu-24.04" ]
jdk: ["17"]
runs-on: ${{ matrix.platform }}
permissions:
contents: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get release tag
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ github.event.inputs.tag }}" ]]; then
TAG="${{ github.event.inputs.tag }}"
# use the latest tag if not specified
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
TAG=$(git ls-remote --tags https://github.com/AutoMQ/automq.git | grep -v '\^{}' | tail -1 | sed 's/.*refs\/tags\///')
else
TAG="${{ github.event.workflow_run.head_branch }}"
fi
AUTOMQ_URL="https://github.com/AutoMQ/automq/releases/download/${TAG}/automq-${TAG}_kafka-${KAFKA_VERSION}.tgz"
{
echo "AUTOMQ_VERSION=${TAG}"
echo "AUTOMQ_URL=${AUTOMQ_URL}"
} >> $GITHUB_ENV
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.jdk }}
distribution: "zulu"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_READ_WRITE_TOKEN }}
- name: Build AutoMQ Strimzi Image
run: |
git clone --depth 1 --branch "${{ env.STRIMZI_BRANCH }}" "${{ env.STRIMZI_REPO }}" strimzi
cd strimzi
chmod +x ./tools/automq/build-automq-image.sh
./tools/automq/build-automq-image.sh \
"${{ env.AUTOMQ_VERSION }}" \
"${{ env.AUTOMQ_URL }}" \
"${{ env.KAFKA_VERSION }}" \
"${{ secrets.DOCKERHUB_USERNAME }}" \
"automq"

View File

@ -46,7 +46,7 @@ jobs:
run: |
python docker_build_test.py kafka/test -tag=test -type=${{ github.event.inputs.image_type }} -u=${{ github.event.inputs.kafka_url }}
- name: Run CVE scan
uses: aquasecurity/trivy-action@master
uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0
with:
image-ref: 'kafka/test:test'
format: 'table'

View File

@ -45,7 +45,7 @@ jobs:
run: |
python docker_official_image_build_test.py kafka/test -tag=test -type=${{ github.event.inputs.image_type }} -v=${{ github.event.inputs.kafka_version }}
- name: Run CVE scan
uses: aquasecurity/trivy-action@master
uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0
with:
image-ref: 'kafka/test:test'
format: 'table'

View File

@ -31,11 +31,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@ -47,11 +47,11 @@ jobs:
python -m pip install --upgrade pip
pip install -r docker/requirements.txt
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@ -29,7 +29,7 @@ jobs:
supported_image_tag: ['latest', '3.7.0']
steps:
- name: Run CVE scan
uses: aquasecurity/trivy-action@master
uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0
if: always()
with:
image-ref: apache/kafka:${{ matrix.supported_image_tag }}

View File

@ -30,32 +30,39 @@ jobs:
uses: gradle/gradle-build-action@v2.12.0
- name: Build TarGz
id: build-targz
run: |
./gradlew -Pprefix=automq-${{ github.ref_name }}_ --build-cache --refresh-dependencies clean releaseTarGz
mkdir -p core/build/distributions/latest
LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
echo "LATEST_TAG=$LATEST_TAG"
IS_LATEST="false"
if [ "$LATEST_TAG" == "${{ github.ref_name }}" ]; then
IS_LATEST=true
fi
echo "IS_LATEST=$IS_LATEST" >> $GITHUB_OUTPUT
for file in core/build/distributions/automq-*.tgz; do
if [[ ! "$file" =~ site-docs ]]; then
echo "Find latest tgz file: $file"
cp "$file" core/build/distributions/latest/automq-kafka-latest.tgz
break
if [ "$IS_LATEST" = "true" ]; then
echo "Find latest tgz file: $file"
cp "$file" core/build/distributions/latest/automq-kafka-latest.tgz
fi
else
echo "Skip and remove site-docs file: $file"
rm "$file"
fi
done
- uses: jakejarvis/s3-sync-action@master
name: s3-upload-latest
if: ${{ github.repository_owner == 'AutoMQ' }}
- uses: tvrcgo/oss-action@master
name: upload-latest
if: ${{ github.repository_owner == 'AutoMQ' && steps.build-targz.outputs.IS_LATEST == 'true' }}
with:
args: --follow-symlinks --delete
env:
AWS_S3_BUCKET: ${{ secrets.AWS_CN_PROD_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_CN_PROD_AK }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_CN_PROD_SK }}
AWS_REGION: 'cn-northwest-1'
SOURCE_DIR: 'core/build/distributions/latest'
DEST_DIR: 'community_edition/artifacts'
bucket: ${{ secrets.UPLOAD_BUCKET }}
key-id: ${{ secrets.UPLOAD_BUCKET_AK }}
key-secret: ${{ secrets.UPLOAD_BUCKET_SK }}
region: 'oss-cn-hangzhou'
assets: |
core/build/distributions/latest/automq-kafka-latest.tgz:community_edition/artifacts/automq-kafka-latest.tgz
- name: GitHub Release
uses: softprops/action-gh-release@v1

View File

@ -41,6 +41,22 @@ jobs:
name: "Run Main E2E Tests 5"
uses: ./.github/workflows/e2e-run.yml
if: ${{ github.repository_owner == 'AutoMQ' }}
with:
suite-id: "main5"
test-yaml: "tests/suites/main_kos_test_suite5.yml"
runner: "e2e"
main_e2e_6:
name: "Run Main E2E Tests 6"
uses: ./.github/workflows/e2e-run.yml
if: ${{ github.repository_owner == 'AutoMQ' }}
with:
suite-id: "main6"
test-yaml: "tests/suites/main_kos_test_suite6.yml"
runner: "e2e"
main_e2e_7:
name: "Run Main E2E Tests 7"
uses: ./.github/workflows/e2e-run.yml
if: ${{ github.repository_owner == 'AutoMQ' }}
with:
suite-id: "main5"
test-path: "tests/kafkatest/automq"
@ -57,5 +73,5 @@ jobs:
CURRENT_REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
WEB_HOOK_URL: ${{ secrets.E2E_REPORT_WEB_HOOK_URL }}
DATA_MAP: "{\"main_e2e_1\": ${{ toJSON(needs.main_e2e_1.outputs) }}, \"main_e2e_2\": ${{ toJSON(needs.main_e2e_2.outputs) }}, \"main_e2e_3\": ${{ toJSON(needs.main_e2e_3.outputs) }}, \"main_e2e_4\": ${{ toJSON(needs.main_e2e_4.outputs) }}, \"main_e2e_5\": ${{ toJSON(needs.main_e2e_5.outputs) }}}"
DATA_MAP: "{\"main_e2e_1\": ${{ toJSON(needs.main_e2e_1.outputs) }}, \"main_e2e_2\": ${{ toJSON(needs.main_e2e_2.outputs) }}, \"main_e2e_3\": ${{ toJSON(needs.main_e2e_3.outputs) }}, \"main_e2e_4\": ${{ toJSON(needs.main_e2e_4.outputs) }}, \"main_e2e_5\": ${{ toJSON(needs.main_e2e_5.outputs) }}, \"main_e2e_6\": ${{ toJSON(needs.main_e2e_6.outputs) }}, \"main_e2e_7\": ${{ toJSON(needs.main_e2e_7.outputs) }}}"
REPORT_TITLE_PREFIX: "Main"

View File

@ -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.
name: Publish Maven Package
on:
workflow_dispatch:
inputs:
version:
description: 'Version to publish'
required: true
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+-rc[0-9]+'
env:
VERSION: ${{ github.event.inputs.version || github.ref_name }}
jobs:
publish:
name: "Publish to Github Packages"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-22.04 ]
jdk: [ 17 ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Gradle wrapper validation
uses: gradle/actions/wrapper-validation@v3
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.jdk }}
distribution: "zulu"
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: '8.10'
- name: Publish
run: |
gradle publish -PmavenUrl='https://maven.pkg.github.com/AutoMQ/automq' \
-PmavenUsername=${{ env.GITHUB_ACTOR }} -PmavenPassword=${{ secrets.GITHUB_TOKEN }} \
-PskipSigning=true \
-Pgroup=com.automq.automq -Pversion=${{ env.VERSION }}

View File

@ -0,0 +1,31 @@
name: Spark Iceberg image
on:
workflow_dispatch:
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_READ_WRITE_TOKEN }}
- name: Build and Push
uses: docker/build-push-action@v6
with:
context: docker/table_topic/spark_iceberg/
platforms: linux/amd64,linux/arm64
push: true
tags: automqinc/spark-iceberg:latest

4
.gitignore vendored
View File

@ -62,3 +62,7 @@ storage/kafka-tiered-storage/
docker/test/report_*.html
kafka.Kafka
__pycache__
# Ignore bin folder generated by the build, but exclude the one in the root
bin/
!/bin/

View File

@ -1,6 +0,0 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright 2024, AutoMQ HK Limited.&#10;&#10;The use of this file is governed by the Business Source License,&#10;as detailed in the file &quot;/LICENSE.S3Stream&quot; included in this repository.&#10;&#10;As of the Change Date specified in that file, in accordance with&#10;the Business Source License, use of this software will be governed&#10;by the Apache License, Version 2.0" />
<option name="myName" value="BSL" />
</copyright>
</component>

View File

@ -1,7 +0,0 @@
<component name="CopyrightManager">
<settings default="BSL">
<module2copyright>
<element module="All" copyright="BSL" />
</module2copyright>
</settings>
</component>

221
LICENSE
View File

@ -1,29 +1,202 @@
Copyright (c) 2023-2024 AutoMQ HK Limited.
this software are licensed as follows:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
1. Apache Kafka Source and Dependency Licensing:
All code in this repository that is forked from Apache Kafka and its
dependencies will continue to be licensed under the original Apache Kafka
open source license. For detailed licensing information regarding Apache
Kafka and its dependencies, please refer to the files under the "/licenses/"
folder in this repository.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
2. S3Stream Component Licensing:
The S3Stream component added to this project (specifically referring to all
files under the "/S3Stream/" directory) is licensed under a revised Business
Source License (BSL) by AutoMQ HK Limited, with the specific terms available
in the /LICENSE.S3Stream file in this repository. Any dependencies used by
the S3Stream component are subject to their respective open source licenses.
1. Definitions.
3. File-Level License Precedence:
For each file in this repository, if the license is explicitly specified in
the header of the file, the license stated in the file header shall prevail.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -1,96 +0,0 @@
License text copyright © 2023 MariaDB plc, All Rights Reserved.
"Business Source License" is a trademark of MariaDB plc.
Parameters
Licensor: AutoMQ HK Limited.
Licensed Work: AutoMQ Version 1.1.2 or later. The Licensed Work is (c) 2024
AutoMQ HK Limited.
Additional Use Grant: You may make production use of the Licensed Work, provided
Your use does not include offering the Licensed Work to third
parties on a hosted or embedded basis in order to compete with
AutoMQ's paid version(s) of the Licensed Work. For purposes
of this license:
A "competitive offering" is a Product that is offered to third
parties on a paid basis, including through paid support
arrangements, that significantly overlaps with the capabilities
of AutoMQ's paid version(s) of the Licensed Work. If Your
Product is not a competitive offering when You first make it
generally available, it will not become a competitive offering
later due to AutoMQ releasing a new version of the Licensed
Work with additional capabilities. In addition, Products that
are not provided on a paid basis are not competitive.
"Product" means software that is offered to end users to manage
in their own environments or offered as a service on a hosted
basis.
"Embedded" means including the source code or executable code
from the Licensed Work in a competitive offering. "Embedded"
also means packaging the competitive offering in such a way
that the Licensed Work must be accessed or downloaded for the
competitive offering to operate.
Hosting or using the Licensed Work(s) for internal purposes
within an organization is not considered a competitive
offering. AutoMQ considers your organization to include all
of your affiliates under common control.
For binding interpretive guidance on using AutoMQ products
under the Business Source License, please visit our FAQ.
(https://www.automq.com/license-faq)
Change Date: Change date is four years from release date.
Please see https://github.com/AutoMQ/automq/releases for exact dates
Change License: Apache License, Version 2.0
URL: https://www.apache.org/licenses/LICENSE-2.0
For information about alternative licensing arrangements for the Licensed Work,
please contact licensing@automq.com.
Notice
Business Source License 1.1
Terms
The Licensor hereby grants you the right to copy, modify, create derivative
works, redistribute, and make non-production use of the Licensed Work. The
Licensor may make an Additional Use Grant, above, permitting limited production use.
Effective on the Change Date, or the fourth anniversary of the first publicly
available distribution of a specific version of the Licensed Work under this
License, whichever comes first, the Licensor hereby grants you rights under
the terms of the Change License, and the rights granted in the paragraph
above terminate.
If your use of the Licensed Work does not comply with the requirements
currently in effect as described in this License, you must purchase a
commercial license from the Licensor, its affiliated entities, or authorized
resellers, or you must refrain from using the Licensed Work.
All copies of the original and modified Licensed Work, and derivative works
of the Licensed Work, are subject to this License. This License applies
separately for each version of the Licensed Work and the Change Date may vary
for each version of the Licensed Work released by Licensor.
You must conspicuously display this License on each original or modified copy
of the Licensed Work. If you receive the Licensed Work in original or
modified form from a third party, the terms and conditions set forth in this
License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically
terminate your rights under this License for the current and all other
versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of
Licensor or its affiliates (provided that you may use a trademark or logo of
Licensor as expressly required by this License).
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
TITLE.

2
NOTICE
View File

@ -1,5 +1,5 @@
AutoMQ NOTICE
Copyright 2023-2024, AutoMQ HK Limited.
Copyright 2023-2025, AutoMQ HK Limited.
---------------------------
Apache Kafka NOTICE

View File

@ -1,5 +1,5 @@
AutoMQ Binary NOTICE
Copyright 2023-2024, AutoMQ HK Limited.
Copyright 2023-2025, AutoMQ HK Limited.
---------------------------
Apache Kafka Binary NOTICE

View File

@ -0,0 +1,125 @@
# AutoMQ Log Uploader Module
This module provides asynchronous S3 log upload capability based on Log4j 1.x. Other submodules only need to depend on this module and configure it simply to synchronize logs to object storage. Core components:
- `com.automq.log.S3RollingFileAppender`: Extends `RollingFileAppender`, pushes log events to the uploader while writing to local files.
- `com.automq.log.uploader.LogUploader`: Asynchronously buffers, compresses, and uploads logs; supports configuration switches and periodic cleanup.
- `com.automq.log.uploader.S3LogConfig`: Interface that abstracts the configuration required for uploading. Implementations must provide cluster ID, node ID, object storage instance, and leadership status.
## Quick Integration
1. Add dependency in your module's `build.gradle`:
```groovy
implementation project(':automq-log-uploader')
```
2. Implement or provide an `S3LogConfig` instance and configure the appender:
```java
// Set up the S3LogConfig through your application
S3LogConfig config = // your S3LogConfig implementation
S3RollingFileAppender.setup(config);
```
3. Reference the Appender in `log4j.properties`:
```properties
log4j.appender.s3_uploader=com.automq.log.S3RollingFileAppender
log4j.appender.s3_uploader.File=logs/server.log
log4j.appender.s3_uploader.MaxFileSize=100MB
log4j.appender.s3_uploader.MaxBackupIndex=10
log4j.appender.s3_uploader.layout=org.apache.log4j.PatternLayout
log4j.appender.s3_uploader.layout.ConversionPattern=[%d] %p %m (%c)%n
```
## S3LogConfig Interface
The `S3LogConfig` interface provides the configuration needed for log uploading:
```java
public interface S3LogConfig {
boolean isEnabled(); // Whether S3 upload is enabled
String clusterId(); // Cluster identifier
int nodeId(); // Node identifier
ObjectStorage objectStorage(); // S3 object storage instance
boolean isLeader(); // Whether this node should upload logs
}
```
The upload schedule can be overridden by environment variables:
- `AUTOMQ_OBSERVABILITY_UPLOAD_INTERVAL`: Maximum upload interval (milliseconds).
- `AUTOMQ_OBSERVABILITY_CLEANUP_INTERVAL`: Retention period (milliseconds), old objects earlier than this time will be cleaned up.
## Implementation Notes
### Leader Selection
The log uploader relies on the `S3LogConfig.isLeader()` method to determine whether the current node should upload logs and perform cleanup tasks. This avoids multiple nodes in a cluster simultaneously executing these operations.
### Object Storage Path
Logs are uploaded to object storage following this path pattern:
```
automq/logs/{clusterId}/{nodeId}/{hour}/{uuid}
```
Where:
- `clusterId` and `nodeId` come from the S3LogConfig
- `hour` is the timestamp hour for log organization
- `uuid` is a unique identifier for each log batch
## Usage Example
Complete example of using the log uploader:
```java
import com.automq.log.S3RollingFileAppender;
import com.automq.log.uploader.S3LogConfig;
import com.automq.stream.s3.operator.ObjectStorage;
// Implement S3LogConfig
public class MyS3LogConfig implements S3LogConfig {
@Override
public boolean isEnabled() {
return true; // Enable S3 upload
}
@Override
public String clusterId() {
return "my-cluster";
}
@Override
public int nodeId() {
return 1;
}
@Override
public ObjectStorage objectStorage() {
// Return your ObjectStorage instance
return myObjectStorage;
}
@Override
public boolean isLeader() {
// Return true if this node should upload logs
return isCurrentNodeLeader();
}
}
// Setup and use
S3LogConfig config = new MyS3LogConfig();
S3RollingFileAppender.setup(config);
// Configure Log4j to use the appender
// The appender will now automatically upload logs to S3
```
## Lifecycle Management
Remember to properly shutdown the log uploader when your application terminates:
```java
// During application shutdown
S3RollingFileAppender.shutdown();
```

View File

@ -0,0 +1,105 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.log;
import com.automq.log.uploader.LogRecorder;
import com.automq.log.uploader.LogUploader;
import com.automq.log.uploader.S3LogConfig;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.LoggingEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class S3RollingFileAppender extends RollingFileAppender {
private static final Logger LOGGER = LoggerFactory.getLogger(S3RollingFileAppender.class);
private static final Object INIT_LOCK = new Object();
private static volatile LogUploader logUploaderInstance;
private static volatile S3LogConfig s3LogConfig;
public S3RollingFileAppender() {
super();
}
public static void setup(S3LogConfig config) {
s3LogConfig = config;
synchronized (INIT_LOCK) {
if (logUploaderInstance != null) {
return;
}
try {
if (s3LogConfig == null) {
LOGGER.error("No s3LogConfig available; S3 log upload remains disabled.");
throw new RuntimeException("S3 log configuration is missing.");
}
if (!s3LogConfig.isEnabled() || s3LogConfig.objectStorage() == null) {
LOGGER.warn("S3 log upload is disabled by configuration.");
return;
}
LogUploader uploader = new LogUploader();
uploader.start(s3LogConfig);
logUploaderInstance = uploader;
LOGGER.info("S3RollingFileAppender initialized successfully using s3LogConfig {}.", s3LogConfig.getClass().getName());
} catch (Exception e) {
LOGGER.error("Failed to initialize S3RollingFileAppender", e);
throw e;
}
}
}
public static void shutdown() {
if (logUploaderInstance != null) {
synchronized (INIT_LOCK) {
if (logUploaderInstance != null) {
try {
logUploaderInstance.close();
logUploaderInstance = null;
LOGGER.info("S3RollingFileAppender log uploader closed successfully.");
} catch (Exception e) {
LOGGER.error("Failed to close S3RollingFileAppender log uploader", e);
}
}
}
}
}
@Override
protected void subAppend(LoggingEvent event) {
super.subAppend(event);
if (!closed && logUploaderInstance != null) {
LogRecorder.LogEvent logEvent = new LogRecorder.LogEvent(
event.getTimeStamp(),
event.getLevel().toString(),
event.getLoggerName(),
event.getRenderedMessage(),
event.getThrowableStrRep());
try {
logEvent.validate();
logUploaderInstance.append(logEvent);
} catch (IllegalArgumentException e) {
errorHandler.error("Failed to validate and append log event", e, 0);
}
}
}
}

View File

@ -1,15 +1,23 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.log;
package com.automq.log.uploader;
import org.apache.commons.lang3.StringUtils;
@ -39,10 +47,10 @@ public interface LogRecorder {
throw new IllegalArgumentException("Level cannot be blank");
}
if (StringUtils.isBlank(logger)) {
throw new IllegalArgumentException("Level cannot be blank");
throw new IllegalArgumentException("Logger cannot be blank");
}
if (StringUtils.isBlank(message)) {
throw new IllegalArgumentException("Level cannot be blank");
throw new IllegalArgumentException("Message cannot be blank");
}
}

View File

@ -1,17 +1,25 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.log;
package com.automq.log.uploader;
import com.automq.shell.AutoMQApplication;
import com.automq.log.uploader.util.Utils;
import com.automq.stream.s3.operator.ObjectStorage;
import com.automq.stream.s3.operator.ObjectStorage.ObjectInfo;
import com.automq.stream.s3.operator.ObjectStorage.ObjectPath;
@ -46,12 +54,14 @@ public class LogUploader implements LogRecorder {
public static final int DEFAULT_MAX_QUEUE_SIZE = 64 * 1024;
public static final int DEFAULT_BUFFER_SIZE = 16 * 1024 * 1024;
public static final int UPLOAD_INTERVAL = System.getenv("AUTOMQ_OBSERVABILITY_UPLOAD_INTERVAL") != null ? Integer.parseInt(System.getenv("AUTOMQ_OBSERVABILITY_UPLOAD_INTERVAL")) : 60 * 1000;
public static final int CLEANUP_INTERVAL = System.getenv("AUTOMQ_OBSERVABILITY_CLEANUP_INTERVAL") != null ? Integer.parseInt(System.getenv("AUTOMQ_OBSERVABILITY_CLEANUP_INTERVAL")) : 2 * 60 * 1000;
public static final int UPLOAD_INTERVAL = System.getenv("AUTOMQ_OBSERVABILITY_UPLOAD_INTERVAL") != null
? Integer.parseInt(System.getenv("AUTOMQ_OBSERVABILITY_UPLOAD_INTERVAL"))
: 60 * 1000;
public static final int CLEANUP_INTERVAL = System.getenv("AUTOMQ_OBSERVABILITY_CLEANUP_INTERVAL") != null
? Integer.parseInt(System.getenv("AUTOMQ_OBSERVABILITY_CLEANUP_INTERVAL"))
: 2 * 60 * 1000;
public static final int MAX_JITTER_INTERVAL = 60 * 1000;
private static final LogUploader INSTANCE = new LogUploader();
private final BlockingQueue<LogEvent> queue = new LinkedBlockingQueue<>(DEFAULT_MAX_QUEUE_SIZE);
private final ByteBuf uploadBuffer = Unpooled.directBuffer(DEFAULT_BUFFER_SIZE);
private final Random random = new Random();
@ -62,16 +72,42 @@ public class LogUploader implements LogRecorder {
private volatile S3LogConfig config;
private volatile CompletableFuture<Void> startFuture;
private ObjectStorage objectStorage;
private Thread uploadThread;
private Thread cleanupThread;
private LogUploader() {
public LogUploader() {
}
public static LogUploader getInstance() {
return INSTANCE;
public synchronized void start(S3LogConfig config) {
if (this.config != null) {
LOGGER.warn("LogUploader is already started.");
return;
}
this.config = config;
if (!config.isEnabled() || config.objectStorage() == null) {
LOGGER.warn("LogUploader is disabled due to configuration.");
closed = true;
return;
}
try {
this.objectStorage = config.objectStorage();
this.uploadThread = new Thread(new UploadTask());
this.uploadThread.setName("log-uploader-upload-thread");
this.uploadThread.setDaemon(true);
this.uploadThread.start();
this.cleanupThread = new Thread(new CleanupTask());
this.cleanupThread.setName("log-uploader-cleanup-thread");
this.cleanupThread.setDaemon(true);
this.cleanupThread.start();
LOGGER.info("LogUploader started successfully.");
} catch (Exception e) {
LOGGER.error("Failed to start LogUploader", e);
closed = true;
}
}
public void close() throws InterruptedException {
@ -88,63 +124,15 @@ public class LogUploader implements LogRecorder {
@Override
public boolean append(LogEvent event) {
if (!closed && couldUpload()) {
if (!closed) {
return queue.offer(event);
}
return false;
}
private boolean couldUpload() {
initConfiguration();
boolean enabled = config != null && config.isEnabled() && config.objectStorage() != null;
if (enabled) {
initUploadComponent();
}
return enabled && startFuture != null && startFuture.isDone();
}
private void initConfiguration() {
if (config == null) {
synchronized (this) {
if (config == null) {
config = AutoMQApplication.getBean(S3LogConfig.class);
}
}
}
}
private void initUploadComponent() {
if (startFuture == null) {
synchronized (this) {
if (startFuture == null) {
startFuture = CompletableFuture.runAsync(() -> {
try {
objectStorage = config.objectStorage();
uploadThread = new Thread(new UploadTask());
uploadThread.setName("log-uploader-upload-thread");
uploadThread.setDaemon(true);
uploadThread.start();
cleanupThread = new Thread(new CleanupTask());
cleanupThread.setName("log-uploader-cleanup-thread");
cleanupThread.setDaemon(true);
cleanupThread.start();
startFuture.complete(null);
} catch (Exception e) {
LOGGER.error("Initialize log uploader failed", e);
}
}, command -> new Thread(command).start());
}
}
}
}
private class UploadTask implements Runnable {
public String formatTimestampInMillis(long timestamp) {
private String formatTimestampInMillis(long timestamp) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault())
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS Z"));
}
@ -156,7 +144,6 @@ public class LogUploader implements LogRecorder {
long now = System.currentTimeMillis();
LogEvent event = queue.poll(1, TimeUnit.SECONDS);
if (event != null) {
// DateTime Level [Logger] Message \n stackTrace
StringBuilder logLine = new StringBuilder()
.append(formatTimestampInMillis(event.timestampMillis()))
.append(" ")
@ -195,25 +182,22 @@ public class LogUploader implements LogRecorder {
private void upload(long now) {
if (uploadBuffer.readableBytes() > 0) {
if (couldUpload()) {
try {
while (!Thread.currentThread().isInterrupted()) {
if (objectStorage == null) {
break;
}
try {
String objectKey = getObjectKey();
objectStorage.write(WriteOptions.DEFAULT, objectKey, uploadBuffer.retainedSlice().asReadOnly()).get();
break;
} catch (Exception e) {
e.printStackTrace(System.err);
Thread.sleep(1000);
}
try {
while (!Thread.currentThread().isInterrupted()) {
if (objectStorage == null) {
break;
}
try {
String objectKey = getObjectKey();
objectStorage.write(WriteOptions.DEFAULT, objectKey, Utils.compress(uploadBuffer.slice().asReadOnly())).get();
break;
} catch (Exception e) {
LOGGER.warn("Failed to upload logs, will retry", e);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
//ignore
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
uploadBuffer.clear();
lastUploadTimestamp = now;
@ -228,12 +212,11 @@ public class LogUploader implements LogRecorder {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
if (closed || !config.isActiveController()) {
if (closed || !config.isLeader()) {
Thread.sleep(Duration.ofMinutes(1).toMillis());
continue;
}
long expiredTime = System.currentTimeMillis() - CLEANUP_INTERVAL;
List<ObjectInfo> objects = objectStorage.list(String.format("automq/logs/%s", config.clusterId())).join();
if (!objects.isEmpty()) {
@ -243,7 +226,6 @@ public class LogUploader implements LogRecorder {
.collect(Collectors.toList());
if (!keyList.isEmpty()) {
// Some of s3 implements allow only 1000 keys per request.
CompletableFuture<?>[] deleteFutures = Lists.partition(keyList, 1000)
.stream()
.map(objectStorage::delete)
@ -251,7 +233,6 @@ public class LogUploader implements LogRecorder {
CompletableFuture.allOf(deleteFutures).join();
}
}
Thread.sleep(Duration.ofMinutes(1).toMillis());
} catch (InterruptedException e) {
break;
@ -266,5 +247,4 @@ public class LogUploader implements LogRecorder {
String hour = LocalDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
return String.format("automq/logs/%s/%s/%s/%s", config.clusterId(), config.nodeId(), hour, UUID.randomUUID());
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.log.uploader;
import com.automq.stream.s3.operator.ObjectStorage;
public interface S3LogConfig {
boolean isEnabled();
String clusterId();
int nodeId();
ObjectStorage objectStorage();
boolean isLeader();
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.log.uploader.util;
import com.automq.stream.s3.ByteBufAlloc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import io.netty.buffer.ByteBuf;
public class Utils {
private Utils() {
}
public static ByteBuf compress(ByteBuf input) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
byte[] buffer = new byte[input.readableBytes()];
input.readBytes(buffer);
gzipOutputStream.write(buffer);
}
ByteBuf compressed = ByteBufAlloc.byteBuffer(byteArrayOutputStream.size());
compressed.writeBytes(byteArrayOutputStream.toByteArray());
return compressed;
}
public static ByteBuf decompress(ByteBuf input) throws IOException {
byte[] compressedData = new byte[input.readableBytes()];
input.readBytes(compressedData);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData);
try (GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = gzipInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
byte[] uncompressedData = byteArrayOutputStream.toByteArray();
ByteBuf output = ByteBufAlloc.byteBuffer(uncompressedData.length);
output.writeBytes(uncompressedData);
return output;
}
}
}

459
automq-metrics/README.md Normal file
View File

@ -0,0 +1,459 @@
# AutoMQ automq-metrics Module
## Module Structure
```
com.automq.opentelemetry/
├── AutoMQTelemetryManager.java # Main management class for initialization and lifecycle
├── TelemetryConstants.java # Constants definition
├── common/
│ ├── OTLPCompressionType.java # OTLP compression types
│ └── OTLPProtocol.java # OTLP protocol types
├── exporter/
│ ├── MetricsExporter.java # Exporter interface
│ ├── MetricsExportConfig.java # Export configuration
│ ├── MetricsExporterProvider.java # Exporter factory provider
│ ├── MetricsExporterType.java # Exporter type enumeration
│ ├── MetricsExporterURI.java # URI parser for exporters
│ ├── OTLPMetricsExporter.java # OTLP exporter implementation
│ ├── PrometheusMetricsExporter.java # Prometheus exporter implementation
│ └── s3/ # S3 metrics exporter implementation
│ ├── CompressionUtils.java # Utility for data compression
│ ├── PrometheusUtils.java # Utilities for Prometheus format
│ ├── S3MetricsExporter.java # S3 metrics exporter implementation
│ └── S3MetricsExporterAdapter.java # Adapter to handle S3 metrics export
└── yammer/
├── DeltaHistogram.java # Delta histogram implementation
├── OTelMetricUtils.java # OpenTelemetry metrics utilities
├── YammerMetricsProcessor.java # Yammer metrics processor
└── YammerMetricsReporter.java # Yammer metrics reporter
```
The AutoMQ OpenTelemetry module is a telemetry data collection and export component based on OpenTelemetry SDK, specifically designed for AutoMQ Kafka. This module provides unified telemetry data management capabilities, supporting the collection of JVM metrics, JMX metrics, and Yammer metrics, and can export data to Prometheus, OTLP-compatible backend systems, or S3-compatible storage.
## Core Features
### 1. Metrics Collection
- **JVM Metrics**: Automatically collect JVM runtime metrics including CPU, memory pools, garbage collection, threads, etc.
- **JMX Metrics**: Define and collect JMX Bean metrics through configuration files
- **Yammer Metrics**: Bridge existing Kafka Yammer metrics system to OpenTelemetry
### 2. Multiple Exporter Support
- **Prometheus**: Expose metrics in Prometheus format through HTTP server
- **OTLP**: Support both gRPC and HTTP/Protobuf protocols for exporting to OTLP backends
- **S3**: Export metrics to S3-compatible object storage systems
### 3. Flexible Configuration
- Support parameter settings through Properties configuration files
- Configurable export intervals, compression methods, timeout values, etc.
- Support metric cardinality limits to control memory usage
## Module Structure
```
com.automq.opentelemetry/
├── AutoMQTelemetryManager.java # Main management class for initialization and lifecycle
├── TelemetryConfig.java # Configuration management class
├── TelemetryConstants.java # Constants definition
├── common/
│ └── MetricsUtils.java # Metrics utility class
├── exporter/
│ ├── MetricsExporter.java # Exporter interface
│ ├── MetricsExporterURI.java # URI parser
<20><><EFBFBD>── OTLPMetricsExporter.java # OTLP exporter implementation
│ ├── PrometheusMetricsExporter.java # Prometheus exporter implementation
│ └── s3/ # S3 metrics exporter implementation
│ ├── CompressionUtils.java # Utility for data compression
│ ├── PrometheusUtils.java # Utilities for Prometheus format
│ ├── S3MetricsConfig.java # Configuration interface
│ ├── S3MetricsExporter.java # S3 metrics exporter implementation
│ ├── S3MetricsExporterAdapter.java # Adapter to handle S3 metrics export
│ ├── LeaderNodeSelector.java # Interface for node selection logic
│ └── LeaderNodeSelectors.java # Factory for node selector implementations
└── yammer/
├── DeltaHistogram.java # Delta histogram implementation
├── OTelMetricUtils.java # OpenTelemetry metrics utilities
├── YammerMetricsProcessor.java # Yammer metrics processor
└── YammerMetricsReporter.java # Yammer metrics reporter
```
## Quick Start
### 1. Basic Usage
```java
import com.automq.opentelemetry.AutoMQTelemetryManager;
import com.automq.opentelemetry.exporter.MetricsExportConfig;
// Implement MetricsExportConfig
public class MyMetricsExportConfig implements MetricsExportConfig {
@Override
public String clusterId() { return "my-cluster"; }
@Override
public boolean isLeader() { return true; }
@Override
public int nodeId() { return 1; }
@Override
public ObjectStorage objectStorage() {
// Return your object storage instance for S3 exports
return myObjectStorage;
}
@Override
public List<Pair<String, String>> baseLabels() {
return Arrays.asList(
Pair.of("environment", "production"),
Pair.of("region", "us-east-1")
);
}
@Override
public int intervalMs() { return 60000; } // 60 seconds
}
// Create export configuration
MetricsExportConfig config = new MyMetricsExportConfig();
// Initialize telemetry manager singleton
AutoMQTelemetryManager manager = AutoMQTelemetryManager.initializeInstance(
"prometheus://localhost:9090", // exporter URI
"automq-kafka", // service name
"broker-1", // instance ID
config // export config
);
// Start Yammer metrics reporting (optional)
MetricsRegistry yammerRegistry = // Get Kafka's Yammer registry
manager.startYammerMetricsReporter(yammerRegistry);
// Application running...
// Shutdown telemetry system
AutoMQTelemetryManager.shutdownInstance();
```
### 2. Get Meter Instance
```java
// Get the singleton instance
AutoMQTelemetryManager manager = AutoMQTelemetryManager.getInstance();
// Get Meter for custom metrics
Meter meter = manager.getMeter();
// Create custom metrics
LongCounter requestCounter = meter
.counterBuilder("http_requests_total")
.setDescription("Total number of HTTP requests")
.build();
requestCounter.add(1, Attributes.of(AttributeKey.stringKey("method"), "GET"));
```
## Configuration
### Basic Configuration
Configuration is provided through the `MetricsExportConfig` interface and constructor parameters:
| Parameter | Description | Example |
|-----------|-------------|---------|
| `exporterUri` | Metrics exporter URI | `prometheus://localhost:9090` |
| `serviceName` | Service name for telemetry | `automq-kafka` |
| `instanceId` | Unique service instance ID | `broker-1` |
| `config` | MetricsExportConfig implementation | See example above |
### Exporter Configuration
All configuration is done through the `MetricsExportConfig` interface and constructor parameters. Export intervals, compression settings, and other options are controlled through:
1. **Exporter URI**: Determines the export destination and protocol
2. **MetricsExportConfig**: Provides cluster information, intervals, and base labels
3. **Constructor parameters**: Service name and instance ID
#### Prometheus Exporter
```java
// Use prometheus:// URI scheme
AutoMQTelemetryManager manager = AutoMQTelemetryManager.initializeInstance(
"prometheus://localhost:9090",
"automq-kafka",
"broker-1",
config
);
```
#### OTLP Exporter
```java
// Use otlp:// URI scheme with optional query parameters
AutoMQTelemetryManager manager = AutoMQTelemetryManager.initializeInstance(
"otlp://localhost:4317?protocol=grpc&compression=gzip&timeout=30000",
"automq-kafka",
"broker-1",
config
);
```
#### S3 Metrics Exporter
```java
// Use s3:// URI scheme
AutoMQTelemetryManager manager = AutoMQTelemetryManager.initializeInstance(
"s3://access-key:secret-key@my-bucket.s3.amazonaws.com",
"automq-kafka",
"broker-1",
config // config.clusterId(), nodeId(), isLeader() used for S3 export
);
```
Example usage with S3 exporter:
```java
// Implementation for S3 export configuration
public class S3MetricsExportConfig implements MetricsExportConfig {
private final ObjectStorage objectStorage;
public S3MetricsExportConfig(ObjectStorage objectStorage) {
this.objectStorage = objectStorage;
}
@Override
public String clusterId() { return "my-kafka-cluster"; }
@Override
public boolean isLeader() {
// Only one node in the cluster should return true
return isCurrentNodeLeader();
}
@Override
public int nodeId() { return 1; }
@Override
public ObjectStorage objectStorage() { return objectStorage; }
@Override
public List<Pair<String, String>> baseLabels() {
return Arrays.asList(Pair.of("environment", "production"));
}
@Override
public int intervalMs() { return 60000; }
}
// Initialize telemetry manager with S3 export
ObjectStorage objectStorage = // Create your object storage instance
MetricsExportConfig config = new S3MetricsExportConfig(objectStorage);
AutoMQTelemetryManager manager = AutoMQTelemetryManager.initializeInstance(
"s3://access-key:secret-key@my-bucket.s3.amazonaws.com",
"automq-kafka",
"broker-1",
config
);
// Application running...
// Shutdown telemetry system
AutoMQTelemetryManager.shutdownInstance();
```
### JMX Metrics Configuration
Define JMX metrics collection rules through YAML configuration files:
```java
AutoMQTelemetryManager manager = AutoMQTelemetryManager.initializeInstance(
exporterUri, serviceName, instanceId, config
);
// Set JMX config paths after initialization
manager.setJmxConfigPaths("/jmx-config.yaml,/kafka-jmx.yaml");
```
#### Configuration File Requirements
1. **Directory Requirements**:
- Configuration files must be placed in the project's classpath (e.g., `src/main/resources` directory)
- Support subdirectory structure, e.g., `/config/jmx-metrics.yaml`
2. **Path Format**:
- Paths must start with `/` to indicate starting from classpath root
- Multiple configuration files separated by commas
3. **File Format**:
- Use YAML format (`.yaml` or `.yml` extension)
- Filenames can be customized, meaningful names are recommended
#### Recommended Directory Structure
```
src/main/resources/
├── jmx-kafka-broker.yaml # Kafka Broker metrics configuration
├── jmx-kafka-consumer.yaml # Kafka Consumer metrics configuration
├── jmx-kafka-producer.yaml # Kafka Producer metrics configuration
└── config/
├── custom-jmx.yaml # Custom JMX metrics configuration
└── third-party-jmx.yaml # Third-party component JMX configuration
```
JMX configuration file example (`jmx-config.yaml`):
```yaml
rules:
- bean: kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec
metricAttribute:
name: kafka_server_broker_topic_messages_in_per_sec
description: Messages in per second
unit: "1/s"
attributes:
- name: topic
value: topic
```
## Supported Metric Types
### 1. JVM Metrics
- Memory usage (heap memory, non-heap memory, memory pools)
- CPU usage
- Garbage collection statistics
- Thread states
### 2. Kafka Metrics
Through Yammer metrics bridging, supports the following types of Kafka metrics:
- `BytesInPerSec` - Bytes input per second
- `BytesOutPerSec` - Bytes output per second
- `Size` - Log size (for identifying idle partitions)
### 3. Custom Metrics
Support creating custom metrics through OpenTelemetry API:
- Counter
- Gauge
- Histogram
- UpDownCounter
## Best Practices
### 1. Production Environment Configuration
```java
public class ProductionMetricsConfig implements MetricsExportConfig {
@Override
public String clusterId() { return "production-cluster"; }
@Override
public boolean isLeader() {
// Implement your leader election logic
return isCurrentNodeController();
}
@Override
public int nodeId() { return getCurrentNodeId(); }
@Override
public ObjectStorage objectStorage() {
return productionObjectStorage;
}
@Override
public List<Pair<String, String>> baseLabels() {
return Arrays.asList(
Pair.of("environment", "production"),
Pair.of("region", System.getenv("AWS_REGION")),
Pair.of("version", getApplicationVersion())
);
}
@Override
public int intervalMs() { return 60000; } // 1 minute
}
// Initialize for production
AutoMQTelemetryManager manager = AutoMQTelemetryManager.initializeInstance(
"prometheus://0.0.0.0:9090", // Or S3 URI for object storage export
"automq-kafka",
System.getenv("HOSTNAME"),
new ProductionMetricsConfig()
);
```
### 2. Development Environment Configuration
```java
public class DevelopmentMetricsConfig implements MetricsExportConfig {
@Override
public String clusterId() { return "dev-cluster"; }
@Override
public boolean isLeader() { return true; } // Single node in dev
@Override
public int nodeId() { return 1; }
@Override
public ObjectStorage objectStorage() { return null; } // Not needed for OTLP
@Override
public List<Pair<String, String>> baseLabels() {
return Arrays.asList(Pair.of("environment", "development"));
}
@Override
public int intervalMs() { return 10000; } // 10 seconds for faster feedback
}
// Initialize for development
AutoMQTelemetryManager manager = AutoMQTelemetryManager.initializeInstance(
"otlp://localhost:4317",
"automq-kafka-dev",
"local-dev",
new DevelopmentMetricsConfig()
);
```
### 3. Resource Management
- Set appropriate metric cardinality limits to avoid memory leaks
- Call `shutdown()` method when application closes to release resources
- Monitor exporter health status
## Troubleshooting
### Common Issues
1. **Metrics not exported**
- Check if exporter URI passed to `initializeInstance()` is correct
- Verify target endpoint is reachable
- Check error messages in logs
- Ensure `MetricsExportConfig.intervalMs()` returns reasonable value
2. **JMX metrics missing**
- Confirm JMX configuration file path set via `setJmxConfigPaths()` is correct
- Check YAML configuration file format
- Verify JMX Bean exists
- Ensure files are in classpath
3. **High memory usage**
- Implement cardinality limits in your `MetricsExportConfig`
- Check for high cardinality labels in `baseLabels()`
- Consider increasing export interval via `intervalMs()`
### Logging Configuration
Enable debug logging for more information using your logging framework configuration (e.g., logback.xml, log4j2.xml):
```xml
<!-- For Logback -->
<logger name="com.automq.opentelemetry" level="DEBUG" />
<logger name="io.opentelemetry" level="INFO" />
```
## Dependencies
- Java 8+
- OpenTelemetry SDK 1.30+
- Apache Commons Lang3
- SLF4J logging framework
## License
This module is open source under the Apache License 2.0.

View File

@ -0,0 +1,330 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry;
import com.automq.opentelemetry.exporter.MetricsExportConfig;
import com.automq.opentelemetry.exporter.MetricsExporter;
import com.automq.opentelemetry.exporter.MetricsExporterURI;
import com.automq.opentelemetry.yammer.YammerMetricsReporter;
import com.yammer.metrics.core.MetricsRegistry;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.instrumentation.jmx.engine.JmxMetricInsight;
import io.opentelemetry.instrumentation.jmx.engine.MetricConfiguration;
import io.opentelemetry.instrumentation.jmx.yaml.RuleParser;
import io.opentelemetry.instrumentation.runtimemetrics.java8.Cpu;
import io.opentelemetry.instrumentation.runtimemetrics.java8.GarbageCollector;
import io.opentelemetry.instrumentation.runtimemetrics.java8.MemoryPools;
import io.opentelemetry.instrumentation.runtimemetrics.java8.Threads;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
import io.opentelemetry.sdk.resources.Resource;
/**
* The main manager for AutoMQ telemetry.
* This class is responsible for initializing, configuring, and managing the lifecycle of all
* telemetry components, including the OpenTelemetry SDK, metric exporters, and various metric sources.
*/
public class AutoMQTelemetryManager {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoMQTelemetryManager.class);
// Singleton instance support
private static volatile AutoMQTelemetryManager instance;
private static final Object LOCK = new Object();
private final String exporterUri;
private final String serviceName;
private final String instanceId;
private final MetricsExportConfig metricsExportConfig;
private final List<MetricReader> metricReaders = new ArrayList<>();
private final List<AutoCloseable> autoCloseableList;
private OpenTelemetrySdk openTelemetrySdk;
private YammerMetricsReporter yammerReporter;
private int metricCardinalityLimit = TelemetryConstants.DEFAULT_METRIC_CARDINALITY_LIMIT;
private String jmxConfigPath;
/**
* Constructs a new Telemetry Manager with the given configuration.
*
* @param exporterUri The metrics exporter URI.
* @param serviceName The service name to be used in telemetry data.
* @param instanceId The unique instance ID for this service instance.
* @param metricsExportConfig The metrics configuration.
*/
public AutoMQTelemetryManager(String exporterUri, String serviceName, String instanceId, MetricsExportConfig metricsExportConfig) {
this.exporterUri = exporterUri;
this.serviceName = serviceName;
this.instanceId = instanceId;
this.metricsExportConfig = metricsExportConfig;
this.autoCloseableList = new ArrayList<>();
// Redirect JUL from OpenTelemetry SDK to SLF4J for unified logging
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
/**
* Gets the singleton instance of AutoMQTelemetryManager.
* Returns null if no instance has been initialized.
*
* @return the singleton instance, or null if not initialized
*/
public static AutoMQTelemetryManager getInstance() {
return instance;
}
/**
* Initializes the singleton instance with the given configuration.
* This method should be called before any other components try to access the instance.
*
* @param exporterUri The metrics exporter URI.
* @param serviceName The service name to be used in telemetry data.
* @param instanceId The unique instance ID for this service instance.
* @param metricsExportConfig The metrics configuration.
* @return the initialized singleton instance
*/
public static AutoMQTelemetryManager initializeInstance(String exporterUri, String serviceName, String instanceId, MetricsExportConfig metricsExportConfig) {
if (instance == null) {
synchronized (LOCK) {
if (instance == null) {
AutoMQTelemetryManager newInstance = new AutoMQTelemetryManager(exporterUri, serviceName, instanceId, metricsExportConfig);
newInstance.init();
instance = newInstance;
LOGGER.info("AutoMQTelemetryManager singleton instance initialized");
}
}
}
return instance;
}
/**
* Shuts down the singleton instance and releases all resources.
*/
public static void shutdownInstance() {
if (instance != null) {
synchronized (LOCK) {
if (instance != null) {
instance.shutdown();
instance = null;
LOGGER.info("AutoMQTelemetryManager singleton instance shutdown");
}
}
}
}
/**
* Initializes the telemetry system. This method sets up the OpenTelemetry SDK,
* configures exporters, and registers JVM and JMX metrics.
*/
public void init() {
SdkMeterProvider meterProvider = buildMeterProvider();
this.openTelemetrySdk = OpenTelemetrySdk.builder()
.setMeterProvider(meterProvider)
.setPropagators(ContextPropagators.create(TextMapPropagator.composite(
W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance())))
.buildAndRegisterGlobal();
// Register JVM and JMX metrics
registerJvmMetrics(openTelemetrySdk);
registerJmxMetrics(openTelemetrySdk);
LOGGER.info("AutoMQ Telemetry Manager initialized successfully.");
}
private SdkMeterProvider buildMeterProvider() {
String hostName;
try {
hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
hostName = "unknown-host";
}
AttributesBuilder attrsBuilder = Attributes.builder()
.put(TelemetryConstants.SERVICE_NAME_KEY, serviceName)
.put(TelemetryConstants.SERVICE_INSTANCE_ID_KEY, instanceId)
.put(TelemetryConstants.HOST_NAME_KEY, hostName)
// Add attributes for Prometheus compatibility
.put(TelemetryConstants.PROMETHEUS_JOB_KEY, serviceName)
.put(TelemetryConstants.PROMETHEUS_INSTANCE_KEY, instanceId);
for (Pair<String, String> label : metricsExportConfig.baseLabels()) {
attrsBuilder.put(label.getKey(), label.getValue());
}
Resource resource = Resource.getDefault().merge(Resource.create(attrsBuilder.build()));
SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder().setResource(resource);
// Configure exporters from URI
MetricsExporterURI exporterURI = buildMetricsExporterURI(exporterUri, metricsExportConfig);
for (MetricsExporter exporter : exporterURI.getMetricsExporters()) {
MetricReader reader = exporter.asMetricReader();
metricReaders.add(reader);
SdkMeterProviderUtil.registerMetricReaderWithCardinalitySelector(meterProviderBuilder, reader,
instrumentType -> metricCardinalityLimit);
}
return meterProviderBuilder.build();
}
protected MetricsExporterURI buildMetricsExporterURI(String exporterUri, MetricsExportConfig metricsExportConfig) {
return MetricsExporterURI.parse(exporterUri, metricsExportConfig);
}
private void registerJvmMetrics(OpenTelemetry openTelemetry) {
autoCloseableList.addAll(MemoryPools.registerObservers(openTelemetry));
autoCloseableList.addAll(Cpu.registerObservers(openTelemetry));
autoCloseableList.addAll(GarbageCollector.registerObservers(openTelemetry));
autoCloseableList.addAll(Threads.registerObservers(openTelemetry));
LOGGER.info("JVM metrics registered.");
}
@SuppressWarnings({"NP_LOAD_OF_KNOWN_NULL_VALUE", "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
private void registerJmxMetrics(OpenTelemetry openTelemetry) {
List<String> jmxConfigPaths = getJmxConfigPaths();
if (jmxConfigPaths.isEmpty()) {
LOGGER.info("No JMX metric config paths provided, skipping JMX metrics registration.");
return;
}
JmxMetricInsight jmxMetricInsight = JmxMetricInsight.createService(openTelemetry, metricsExportConfig.intervalMs());
MetricConfiguration metricConfig = new MetricConfiguration();
for (String path : jmxConfigPaths) {
try (InputStream ins = this.getClass().getResourceAsStream(path)) {
if (ins == null) {
LOGGER.error("JMX config file not found in classpath: {}", path);
continue;
}
RuleParser parser = RuleParser.get();
parser.addMetricDefsTo(metricConfig, ins, path);
} catch (Exception e) {
LOGGER.error("Failed to parse JMX config file: {}", path, e);
}
}
jmxMetricInsight.start(metricConfig);
// JmxMetricInsight doesn't implement Closeable, but we can create a wrapper
LOGGER.info("JMX metrics registered with config paths: {}", jmxConfigPaths);
}
public List<String> getJmxConfigPaths() {
if (StringUtils.isEmpty(jmxConfigPath)) {
return Collections.emptyList();
}
return Stream.of(jmxConfigPath.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
}
/**
* Starts reporting metrics from a given Yammer MetricsRegistry.
*
* @param registry The Yammer registry to bridge metrics from.
*/
public void startYammerMetricsReporter(MetricsRegistry registry) {
if (this.openTelemetrySdk == null) {
throw new IllegalStateException("TelemetryManager is not initialized. Call init() first.");
}
if (registry == null) {
LOGGER.warn("Yammer MetricsRegistry is null, skipping reporter start.");
return;
}
this.yammerReporter = new YammerMetricsReporter(registry);
this.yammerReporter.start(getMeter());
}
public void shutdown() {
autoCloseableList.forEach(autoCloseable -> {
try {
autoCloseable.close();
} catch (Exception e) {
LOGGER.error("Failed to close auto closeable", e);
}
});
metricReaders.forEach(metricReader -> {
metricReader.forceFlush();
try {
metricReader.close();
} catch (IOException e) {
LOGGER.error("Failed to close metric reader", e);
}
});
if (openTelemetrySdk != null) {
openTelemetrySdk.close();
}
}
/**
* get YammerMetricsReporter instance.
*
* @return The YammerMetricsReporter instance.
*/
public YammerMetricsReporter getYammerReporter() {
return this.yammerReporter;
}
public void setMetricCardinalityLimit(int limit) {
this.metricCardinalityLimit = limit;
}
public void setJmxConfigPaths(String jmxConfigPaths) {
this.jmxConfigPath = jmxConfigPaths;
}
/**
* Gets the default meter from the initialized OpenTelemetry SDK.
*
* @return The meter instance.
*/
public Meter getMeter() {
if (this.openTelemetrySdk == null) {
throw new IllegalStateException("TelemetryManager is not initialized. Call init() first.");
}
return this.openTelemetrySdk.getMeter(TelemetryConstants.TELEMETRY_SCOPE_NAME);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry;
import io.opentelemetry.api.common.AttributeKey;
/**
* Constants for telemetry, including configuration keys, attribute keys, and default values.
*/
public class TelemetryConstants {
//################################################################
// Service and Resource Attributes
//################################################################
public static final String SERVICE_NAME_KEY = "service.name";
public static final String SERVICE_INSTANCE_ID_KEY = "service.instance.id";
public static final String HOST_NAME_KEY = "host.name";
public static final String TELEMETRY_SCOPE_NAME = "automq_for_kafka";
/**
* The cardinality limit for any single metric.
*/
public static final String METRIC_CARDINALITY_LIMIT_KEY = "automq.telemetry.metric.cardinality.limit";
public static final int DEFAULT_METRIC_CARDINALITY_LIMIT = 20000;
//################################################################
// Prometheus specific Attributes, for compatibility
//################################################################
public static final String PROMETHEUS_JOB_KEY = "job";
public static final String PROMETHEUS_INSTANCE_KEY = "instance";
//################################################################
// Custom Kafka-related Attribute Keys
//################################################################
public static final AttributeKey<Long> START_OFFSET_KEY = AttributeKey.longKey("startOffset");
public static final AttributeKey<Long> END_OFFSET_KEY = AttributeKey.longKey("endOffset");
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.common;
public enum OTLPCompressionType {
GZIP("gzip"),
NONE("none");
private final String type;
OTLPCompressionType(String type) {
this.type = type;
}
public String getType() {
return type;
}
public static OTLPCompressionType fromString(String type) {
for (OTLPCompressionType compressionType : OTLPCompressionType.values()) {
if (compressionType.getType().equalsIgnoreCase(type)) {
return compressionType;
}
}
throw new IllegalArgumentException("Invalid OTLP compression type: " + type);
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.common;
public enum OTLPProtocol {
GRPC("grpc"),
HTTP("http");
private final String protocol;
OTLPProtocol(String protocol) {
this.protocol = protocol;
}
public String getProtocol() {
return protocol;
}
public static OTLPProtocol fromString(String protocol) {
for (OTLPProtocol otlpProtocol : OTLPProtocol.values()) {
if (otlpProtocol.getProtocol().equalsIgnoreCase(protocol)) {
return otlpProtocol;
}
}
throw new IllegalArgumentException("Invalid OTLP protocol: " + protocol);
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.exporter;
import com.automq.stream.s3.operator.ObjectStorage;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
/**
* Configuration interface for metrics exporter.
*/
public interface MetricsExportConfig {
/**
* Get the cluster ID.
* @return The cluster ID.
*/
String clusterId();
/**
* Check if the current node is a primary node for metrics upload.
* @return True if the current node should upload metrics, false otherwise.
*/
boolean isLeader();
/**
* Get the node ID.
* @return The node ID.
*/
int nodeId();
/**
* Get the object storage instance.
* @return The object storage instance.
*/
ObjectStorage objectStorage();
/**
* Get the base labels to include in all metrics.
* @return The base labels.
*/
List<Pair<String, String>> baseLabels();
/**
* Get the interval in milliseconds for metrics export.
* @return The interval in milliseconds.
*/
int intervalMs();
}

View File

@ -1,4 +1,6 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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.
@ -14,16 +16,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.raft.errors;
package com.automq.opentelemetry.exporter;
import io.opentelemetry.sdk.metrics.export.MetricReader;
/**
* Indicates that an append operation cannot be completed because it would have resulted in an
* unexpected base offset.
* An interface for metrics exporters, which can be converted to an OpenTelemetry MetricReader.
*/
public class UnexpectedBaseOffsetException extends RaftException {
private static final long serialVersionUID = 1L;
public UnexpectedBaseOffsetException(String s) {
super(s);
}
public interface MetricsExporter {
MetricReader asMetricReader();
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.exporter;
import java.net.URI;
import java.util.List;
import java.util.Map;
/**
* Service Provider Interface that allows extending the available metrics exporters
* without modifying the core AutoMQ OpenTelemetry module.
*/
public interface MetricsExporterProvider {
/**
* @param scheme exporter scheme (e.g. "rw")
* @return true if this provider can create an exporter for the supplied scheme
*/
boolean supports(String scheme);
/**
* Creates a metrics exporter for the provided URI.
*
* @param config metrics configuration
* @param uri original exporter URI
* @param queryParameters parsed query parameters from the URI
* @return a MetricsExporter instance, or {@code null} if unable to create one
*/
MetricsExporter create(MetricsExportConfig config, URI uri, Map<String, List<String>> queryParameters);
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.exporter;
public enum MetricsExporterType {
OTLP("otlp"),
PROMETHEUS("prometheus"),
OPS("ops"),
OTHER("other");
private final String type;
MetricsExporterType(String type) {
this.type = type;
}
public String getType() {
return type;
}
public static MetricsExporterType fromString(String type) {
for (MetricsExporterType exporterType : MetricsExporterType.values()) {
if (exporterType.getType().equalsIgnoreCase(type)) {
return exporterType;
}
}
return OTHER;
}
}

View File

@ -0,0 +1,220 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.exporter;
import com.automq.opentelemetry.common.OTLPCompressionType;
import com.automq.opentelemetry.common.OTLPProtocol;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
/**
* Parses the exporter URI and creates the corresponding MetricsExporter instances.
*/
public class MetricsExporterURI {
private static final Logger LOGGER = LoggerFactory.getLogger(MetricsExporterURI.class);
private static final List<MetricsExporterProvider> PROVIDERS;
static {
List<MetricsExporterProvider> providers = new ArrayList<>();
ServiceLoader.load(MetricsExporterProvider.class).forEach(providers::add);
PROVIDERS = Collections.unmodifiableList(providers);
if (!PROVIDERS.isEmpty()) {
LOGGER.info("Loaded {} telemetry exporter providers", PROVIDERS.size());
}
}
private final List<MetricsExporter> metricsExporters;
private MetricsExporterURI(List<MetricsExporter> metricsExporters) {
this.metricsExporters = metricsExporters != null ? metricsExporters : new ArrayList<>();
}
public List<MetricsExporter> getMetricsExporters() {
return metricsExporters;
}
public static MetricsExporterURI parse(String uriStr, MetricsExportConfig config) {
LOGGER.info("Parsing metrics exporter URI: {}", uriStr);
if (StringUtils.isBlank(uriStr)) {
LOGGER.info("Metrics exporter URI is not configured, no metrics will be exported.");
return new MetricsExporterURI(Collections.emptyList());
}
// Support multiple exporters separated by comma
String[] exporterUris = uriStr.split(",");
if (exporterUris.length == 0) {
return new MetricsExporterURI(Collections.emptyList());
}
List<MetricsExporter> exporters = new ArrayList<>();
for (String uri : exporterUris) {
if (StringUtils.isBlank(uri)) {
continue;
}
MetricsExporter exporter = parseExporter(config, uri.trim());
if (exporter != null) {
exporters.add(exporter);
}
}
return new MetricsExporterURI(exporters);
}
public static MetricsExporter parseExporter(MetricsExportConfig config, String uriStr) {
try {
URI uri = new URI(uriStr);
String type = uri.getScheme();
if (StringUtils.isBlank(type)) {
LOGGER.error("Invalid metrics exporter URI: {}, exporter scheme is missing", uriStr);
throw new IllegalArgumentException("Invalid metrics exporter URI: " + uriStr);
}
Map<String, List<String>> queries = parseQueryParameters(uri);
return parseExporter(config, type, queries, uri);
} catch (Exception e) {
LOGGER.warn("Parse metrics exporter URI {} failed", uriStr, e);
throw new IllegalArgumentException("Invalid metrics exporter URI: " + uriStr, e);
}
}
public static MetricsExporter parseExporter(MetricsExportConfig config, String type, Map<String, List<String>> queries, URI uri) {
MetricsExporterType exporterType = MetricsExporterType.fromString(type);
switch (exporterType) {
case PROMETHEUS:
return buildPrometheusExporter(config, queries, uri);
case OTLP:
return buildOtlpExporter(config, queries, uri);
case OPS:
return buildS3MetricsExporter(config, uri);
default:
break;
}
MetricsExporterProvider provider = findProvider(type);
if (provider != null) {
MetricsExporter exporter = provider.create(config, uri, queries);
if (exporter != null) {
return exporter;
}
}
LOGGER.warn("Unsupported metrics exporter type: {}", type);
return null;
}
private static MetricsExporter buildPrometheusExporter(MetricsExportConfig config, Map<String, List<String>> queries, URI uri) {
// Use query parameters if available, otherwise fall back to URI authority or config defaults
String host = getStringFromQuery(queries, "host", uri.getHost());
if (StringUtils.isBlank(host)) {
host = "localhost";
}
int port = uri.getPort();
if (port <= 0) {
String portStr = getStringFromQuery(queries, "port", null);
if (StringUtils.isNotBlank(portStr)) {
try {
port = Integer.parseInt(portStr);
} catch (NumberFormatException e) {
LOGGER.warn("Invalid port in query parameters: {}, using default", portStr);
port = 9090;
}
} else {
port = 9090;
}
}
return new PrometheusMetricsExporter(host, port, config.baseLabels());
}
private static MetricsExporter buildOtlpExporter(MetricsExportConfig config, Map<String, List<String>> queries, URI uri) {
// Get endpoint from query parameters or construct from URI
String endpoint = getStringFromQuery(queries, "endpoint", null);
if (StringUtils.isBlank(endpoint)) {
endpoint = uri.getScheme() + "://" + uri.getAuthority();
}
// Get protocol from query parameters or config
String protocol = getStringFromQuery(queries, "protocol", OTLPProtocol.GRPC.getProtocol());
// Get compression from query parameters or config
String compression = getStringFromQuery(queries, "compression", OTLPCompressionType.NONE.getType());
return new OTLPMetricsExporter(config.intervalMs(), endpoint, protocol, compression);
}
private static MetricsExporter buildS3MetricsExporter(MetricsExportConfig config, URI uri) {
LOGGER.info("Creating S3 metrics exporter from URI: {}", uri);
if (config.objectStorage() == null) {
LOGGER.warn("No object storage configured, skip s3 metrics exporter creation.");
return null;
}
// Create the S3MetricsExporterAdapter with appropriate configuration
return new com.automq.opentelemetry.exporter.s3.S3MetricsExporterAdapter(config);
}
private static Map<String, List<String>> parseQueryParameters(URI uri) {
Map<String, List<String>> queries = new HashMap<>();
String query = uri.getQuery();
if (StringUtils.isNotBlank(query)) {
String[] pairs = query.split("&");
for (String pair : pairs) {
String[] keyValue = pair.split("=", 2);
if (keyValue.length == 2) {
String key = keyValue[0];
String value = keyValue[1];
queries.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
}
}
}
return queries;
}
private static String getStringFromQuery(Map<String, List<String>> queries, String key, String defaultValue) {
List<String> values = queries.get(key);
if (values != null && !values.isEmpty()) {
return values.get(0);
}
return defaultValue;
}
private static MetricsExporterProvider findProvider(String scheme) {
for (MetricsExporterProvider provider : PROVIDERS) {
try {
if (provider.supports(scheme)) {
return provider;
}
} catch (Exception e) {
LOGGER.warn("Telemetry exporter provider {} failed to evaluate support for scheme {}", provider.getClass().getName(), scheme, e);
}
}
return null;
}
}

View File

@ -1,18 +1,28 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 kafka.log.stream.s3.telemetry.exporter;
package com.automq.opentelemetry.exporter;
import org.apache.kafka.common.utils.Utils;
import com.automq.opentelemetry.common.OTLPCompressionType;
import com.automq.opentelemetry.common.OTLPProtocol;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -28,13 +38,16 @@ import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder;
public class OTLPMetricsExporter implements MetricsExporter {
private static final Logger LOGGER = LoggerFactory.getLogger(OTLPMetricsExporter.class);
private final int intervalMs;
private final long intervalMs;
private final String endpoint;
private final OTLPProtocol protocol;
private final OTLPCompressionType compression;
// Default timeout for OTLP exporters
private static final long DEFAULT_EXPORTER_TIMEOUT_MS = 30000;
public OTLPMetricsExporter(int intervalMs, String endpoint, String protocol, String compression) {
if (Utils.isBlank(endpoint) || "null".equals(endpoint)) {
public OTLPMetricsExporter(long intervalMs, String endpoint, String protocol, String compression) {
if (StringUtils.isBlank(endpoint) || "null".equals(endpoint)) {
throw new IllegalArgumentException("OTLP endpoint is required");
}
this.intervalMs = intervalMs;
@ -42,7 +55,7 @@ public class OTLPMetricsExporter implements MetricsExporter {
this.protocol = OTLPProtocol.fromString(protocol);
this.compression = OTLPCompressionType.fromString(compression);
LOGGER.info("OTLPMetricsExporter initialized with endpoint: {}, protocol: {}, compression: {}, intervalMs: {}",
endpoint, protocol, compression, intervalMs);
endpoint, protocol, compression, intervalMs);
}
public String endpoint() {
@ -57,31 +70,29 @@ public class OTLPMetricsExporter implements MetricsExporter {
return compression;
}
public int intervalMs() {
public long intervalMs() {
return intervalMs;
}
@Override
public MetricReader asMetricReader() {
PeriodicMetricReaderBuilder builder;
switch (protocol) {
case GRPC:
PeriodicMetricReaderBuilder builder = switch (protocol) {
case GRPC -> {
OtlpGrpcMetricExporterBuilder otlpExporterBuilder = OtlpGrpcMetricExporter.builder()
.setEndpoint(endpoint)
.setCompression(compression.getType())
.setTimeout(Duration.ofMillis(ExporterConstants.DEFAULT_EXPORTER_TIMEOUT_MS));
builder = PeriodicMetricReader.builder(otlpExporterBuilder.build());
break;
case HTTP:
.setTimeout(Duration.ofMillis(DEFAULT_EXPORTER_TIMEOUT_MS));
yield PeriodicMetricReader.builder(otlpExporterBuilder.build());
}
case HTTP -> {
OtlpHttpMetricExporterBuilder otlpHttpExporterBuilder = OtlpHttpMetricExporter.builder()
.setEndpoint(endpoint)
.setCompression(compression.getType())
.setTimeout(Duration.ofMillis(ExporterConstants.DEFAULT_EXPORTER_TIMEOUT_MS));
builder = PeriodicMetricReader.builder(otlpHttpExporterBuilder.build());
break;
default:
throw new IllegalArgumentException("Unsupported OTLP protocol: " + protocol);
}
.setTimeout(Duration.ofMillis(DEFAULT_EXPORTER_TIMEOUT_MS));
yield PeriodicMetricReader.builder(otlpHttpExporterBuilder.build());
}
default -> throw new IllegalArgumentException("Unsupported OTLP protocol: " + protocol);
};
return builder.setInterval(Duration.ofMillis(intervalMs)).build();
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.exporter;
import com.automq.opentelemetry.TelemetryConstants;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import io.opentelemetry.exporter.prometheus.PrometheusHttpServer;
import io.opentelemetry.sdk.metrics.export.MetricReader;
public class PrometheusMetricsExporter implements MetricsExporter {
private static final Logger LOGGER = LoggerFactory.getLogger(PrometheusMetricsExporter.class);
private final String host;
private final int port;
private final Set<String> baseLabelKeys;
public PrometheusMetricsExporter(String host, int port, List<Pair<String, String>> baseLabels) {
if (host == null || host.isEmpty()) {
throw new IllegalArgumentException("Illegal Prometheus host");
}
if (port <= 0) {
throw new IllegalArgumentException("Illegal Prometheus port");
}
this.host = host;
this.port = port;
this.baseLabelKeys = baseLabels.stream().map(Pair::getKey).collect(Collectors.toSet());
LOGGER.info("PrometheusMetricsExporter initialized with host: {}, port: {}, labels: {}", host, port, baseLabels);
}
@Override
public MetricReader asMetricReader() {
return PrometheusHttpServer.builder()
.setHost(host)
.setPort(port)
// This filter is to align with the original behavior, allowing only specific resource attributes
// to be converted to prometheus labels.
.setAllowedResourceAttributesFilter(resourceAttributeKey ->
TelemetryConstants.PROMETHEUS_JOB_KEY.equals(resourceAttributeKey)
|| TelemetryConstants.PROMETHEUS_INSTANCE_KEY.equals(resourceAttributeKey)
|| TelemetryConstants.HOST_NAME_KEY.equals(resourceAttributeKey)
|| baseLabelKeys.contains(resourceAttributeKey))
.build();
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.exporter.s3;
import com.automq.stream.s3.ByteBufAlloc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import io.netty.buffer.ByteBuf;
/**
* Utility class for data compression and decompression.
*/
public class CompressionUtils {
/**
* Compress a ByteBuf using GZIP.
*
* @param input The input ByteBuf to compress.
* @return A new ByteBuf containing the compressed data.
* @throws IOException If an I/O error occurs during compression.
*/
public static ByteBuf compress(ByteBuf input) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
byte[] buffer = new byte[input.readableBytes()];
input.readBytes(buffer);
gzipOutputStream.write(buffer);
gzipOutputStream.close();
ByteBuf compressed = ByteBufAlloc.byteBuffer(byteArrayOutputStream.size());
compressed.writeBytes(byteArrayOutputStream.toByteArray());
return compressed;
}
/**
* Decompress a GZIP-compressed ByteBuf.
*
* @param input The compressed ByteBuf to decompress.
* @return A new ByteBuf containing the decompressed data.
* @throws IOException If an I/O error occurs during decompression.
*/
public static ByteBuf decompress(ByteBuf input) throws IOException {
byte[] compressedData = new byte[input.readableBytes()];
input.readBytes(compressedData);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData);
GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = gzipInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
gzipInputStream.close();
byteArrayOutputStream.close();
byte[] uncompressedData = byteArrayOutputStream.toByteArray();
ByteBuf output = ByteBufAlloc.byteBuffer(uncompressedData.length);
output.writeBytes(uncompressedData);
return output;
}
}

View File

@ -0,0 +1,276 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.exporter.s3;
import org.apache.commons.lang3.StringUtils;
import java.util.Locale;
/**
* Utility class for Prometheus metric and label naming.
*/
public class PrometheusUtils {
private static final String TOTAL_SUFFIX = "_total";
/**
* Get the Prometheus unit from the OpenTelemetry unit.
*
* @param unit The OpenTelemetry unit.
* @return The Prometheus unit.
*/
public static String getPrometheusUnit(String unit) {
if (unit.contains("{")) {
return "";
}
switch (unit) {
// Time
case "d":
return "days";
case "h":
return "hours";
case "min":
return "minutes";
case "s":
return "seconds";
case "ms":
return "milliseconds";
case "us":
return "microseconds";
case "ns":
return "nanoseconds";
// Bytes
case "By":
return "bytes";
case "KiBy":
return "kibibytes";
case "MiBy":
return "mebibytes";
case "GiBy":
return "gibibytes";
case "TiBy":
return "tibibytes";
case "KBy":
return "kilobytes";
case "MBy":
return "megabytes";
case "GBy":
return "gigabytes";
case "TBy":
return "terabytes";
// SI
case "m":
return "meters";
case "V":
return "volts";
case "A":
return "amperes";
case "J":
return "joules";
case "W":
return "watts";
case "g":
return "grams";
// Misc
case "Cel":
return "celsius";
case "Hz":
return "hertz";
case "1":
return "";
case "%":
return "percent";
// Rate units (per second)
case "1/s":
return "per_second";
case "By/s":
return "bytes_per_second";
case "KiBy/s":
return "kibibytes_per_second";
case "MiBy/s":
return "mebibytes_per_second";
case "GiBy/s":
return "gibibytes_per_second";
case "KBy/s":
return "kilobytes_per_second";
case "MBy/s":
return "megabytes_per_second";
case "GBy/s":
return "gigabytes_per_second";
// Rate units (per minute)
case "1/min":
return "per_minute";
case "By/min":
return "bytes_per_minute";
// Rate units (per hour)
case "1/h":
return "per_hour";
case "By/h":
return "bytes_per_hour";
// Rate units (per day)
case "1/d":
return "per_day";
case "By/d":
return "bytes_per_day";
default:
return unit;
}
}
/**
* Map a metric name to a Prometheus-compatible name.
*
* @param name The original metric name.
* @param unit The metric unit.
* @param isCounter Whether the metric is a counter.
* @param isGauge Whether the metric is a gauge.
* @return The Prometheus-compatible metric name.
*/
public static String mapMetricsName(String name, String unit, boolean isCounter, boolean isGauge) {
// Replace "." into "_"
name = name.replaceAll("\\.", "_");
String prometheusUnit = getPrometheusUnit(unit);
boolean shouldAppendUnit = StringUtils.isNotBlank(prometheusUnit) && !name.contains(prometheusUnit);
// append prometheus unit if not null or empty.
// unit should be appended before type suffix
if (shouldAppendUnit) {
name = name + "_" + prometheusUnit;
}
// trim counter's _total suffix so the unit is placed before it.
if (isCounter && name.endsWith(TOTAL_SUFFIX)) {
name = name.substring(0, name.length() - TOTAL_SUFFIX.length());
}
// replace _total suffix, or add if it wasn't already present.
if (isCounter) {
name = name + TOTAL_SUFFIX;
}
// special case - gauge with intelligent Connect metric handling
if ("1".equals(unit) && isGauge && !name.contains("ratio")) {
if (isConnectMetric(name)) {
// For Connect metrics, use improved logic to avoid misleading _ratio suffix
if (shouldAddRatioSuffixForConnect(name)) {
name = name + "_ratio";
}
} else {
// For other metrics, maintain original behavior
name = name + "_ratio";
}
}
return name;
}
/**
* Map a label name to a Prometheus-compatible name.
*
* @param name The original label name.
* @return The Prometheus-compatible label name.
*/
public static String mapLabelName(String name) {
if (StringUtils.isBlank(name)) {
return "";
}
return name.replaceAll("\\.", "_");
}
/**
* Check if a metric name is related to Kafka Connect.
*
* @param name The metric name to check.
* @return true if it's a Connect metric, false otherwise.
*/
private static boolean isConnectMetric(String name) {
String lowerName = name.toLowerCase(Locale.ROOT);
return lowerName.contains("kafka_connector_") ||
lowerName.contains("kafka_task_") ||
lowerName.contains("kafka_worker_") ||
lowerName.contains("kafka_connect_") ||
lowerName.contains("kafka_source_task_") ||
lowerName.contains("kafka_sink_task_") ||
lowerName.contains("connector_metrics") ||
lowerName.contains("task_metrics") ||
lowerName.contains("worker_metrics") ||
lowerName.contains("source_task_metrics") ||
lowerName.contains("sink_task_metrics");
}
/**
* Intelligently determine if a Connect metric should have a _ratio suffix.
* This method avoids adding misleading _ratio suffixes to count-based metrics.
*
* @param name The metric name to check.
* @return true if _ratio suffix should be added, false otherwise.
*/
private static boolean shouldAddRatioSuffixForConnect(String name) {
String lowerName = name.toLowerCase(Locale.ROOT);
if (hasRatioRelatedWords(lowerName)) {
return false;
}
if (isCountMetric(lowerName)) {
return false;
}
return isRatioMetric(lowerName);
}
private static boolean hasRatioRelatedWords(String lowerName) {
return lowerName.contains("ratio") || lowerName.contains("percent") ||
lowerName.contains("rate") || lowerName.contains("fraction");
}
private static boolean isCountMetric(String lowerName) {
return hasBasicCountKeywords(lowerName) || hasConnectCountKeywords(lowerName) ||
hasStatusCountKeywords(lowerName);
}
private static boolean hasBasicCountKeywords(String lowerName) {
return lowerName.contains("count") || lowerName.contains("num") ||
lowerName.contains("size") || lowerName.contains("total") ||
lowerName.contains("active") || lowerName.contains("current");
}
private static boolean hasConnectCountKeywords(String lowerName) {
return lowerName.contains("partition") || lowerName.contains("task") ||
lowerName.contains("connector") || lowerName.contains("seq_no") ||
lowerName.contains("seq_num") || lowerName.contains("attempts");
}
private static boolean hasStatusCountKeywords(String lowerName) {
return lowerName.contains("success") || lowerName.contains("failure") ||
lowerName.contains("errors") || lowerName.contains("retries") ||
lowerName.contains("skipped") || lowerName.contains("running") ||
lowerName.contains("paused") || lowerName.contains("failed") ||
lowerName.contains("destroyed");
}
private static boolean isRatioMetric(String lowerName) {
return lowerName.contains("utilization") ||
lowerName.contains("usage") ||
lowerName.contains("load") ||
lowerName.contains("efficiency") ||
lowerName.contains("hit_rate") ||
lowerName.contains("miss_rate");
}
}

View File

@ -1,20 +1,30 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.metrics;
package com.automq.opentelemetry.exporter.s3;
import com.automq.opentelemetry.exporter.MetricsExportConfig;
import com.automq.stream.s3.operator.ObjectStorage;
import com.automq.stream.s3.operator.ObjectStorage.ObjectInfo;
import com.automq.stream.s3.operator.ObjectStorage.ObjectPath;
import com.automq.stream.s3.operator.ObjectStorage.WriteOptions;
import com.automq.stream.utils.Threads;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -50,6 +60,9 @@ import io.opentelemetry.sdk.metrics.data.HistogramPointData;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
/**
* An S3 metrics exporter that uploads metrics data to S3 buckets.
*/
public class S3MetricsExporter implements MetricExporter {
private static final Logger LOGGER = LoggerFactory.getLogger(S3MetricsExporter.class);
@ -58,13 +71,13 @@ public class S3MetricsExporter implements MetricExporter {
public static final int MAX_JITTER_INTERVAL = 60 * 1000;
public static final int DEFAULT_BUFFER_SIZE = 16 * 1024 * 1024;
private final S3MetricsConfig config;
private final MetricsExportConfig config;
private final Map<String, String> defaultTagMap = new HashMap<>();
private final ByteBuf uploadBuffer = Unpooled.directBuffer(DEFAULT_BUFFER_SIZE);
private final Random random = new Random();
private static final Random RANDOM = new Random();
private volatile long lastUploadTimestamp = System.currentTimeMillis();
private volatile long nextUploadInterval = UPLOAD_INTERVAL + random.nextInt(MAX_JITTER_INTERVAL);
private volatile long nextUploadInterval = UPLOAD_INTERVAL + RANDOM.nextInt(MAX_JITTER_INTERVAL);
private final ObjectStorage objectStorage;
private final ObjectMapper objectMapper = new ObjectMapper();
@ -73,7 +86,12 @@ public class S3MetricsExporter implements MetricExporter {
private final Thread uploadThread;
private final Thread cleanupThread;
public S3MetricsExporter(S3MetricsConfig config) {
/**
* Creates a new S3MetricsExporter.
*
* @param config The configuration for the S3 metrics exporter.
*/
public S3MetricsExporter(MetricsExportConfig config) {
this.config = config;
this.objectStorage = config.objectStorage();
@ -91,6 +109,9 @@ public class S3MetricsExporter implements MetricExporter {
cleanupThread.setDaemon(true);
}
/**
* Starts the exporter threads.
*/
public void start() {
uploadThread.start();
cleanupThread.start();
@ -129,7 +150,7 @@ public class S3MetricsExporter implements MetricExporter {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
if (closed || !config.isActiveController()) {
if (closed || !config.isLeader()) {
Thread.sleep(Duration.ofMinutes(1).toMillis());
continue;
}
@ -152,8 +173,7 @@ public class S3MetricsExporter implements MetricExporter {
CompletableFuture.allOf(deleteFutures).join();
}
}
Thread.sleep(Duration.ofMinutes(1).toMillis());
Threads.sleep(Duration.ofMinutes(1).toMillis());
} catch (InterruptedException e) {
break;
} catch (Exception e) {
@ -242,13 +262,13 @@ public class S3MetricsExporter implements MetricExporter {
synchronized (uploadBuffer) {
if (uploadBuffer.readableBytes() > 0) {
try {
objectStorage.write(WriteOptions.DEFAULT, getObjectKey(), uploadBuffer.retainedSlice().asReadOnly()).get();
objectStorage.write(WriteOptions.DEFAULT, getObjectKey(), CompressionUtils.compress(uploadBuffer.slice().asReadOnly())).get();
} catch (Exception e) {
LOGGER.error("Failed to upload metrics to s3", e);
return CompletableResultCode.ofFailure();
} finally {
lastUploadTimestamp = System.currentTimeMillis();
nextUploadInterval = UPLOAD_INTERVAL + random.nextInt(MAX_JITTER_INTERVAL);
nextUploadInterval = UPLOAD_INTERVAL + RANDOM.nextInt(MAX_JITTER_INTERVAL);
uploadBuffer.clear();
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.exporter.s3;
import com.automq.opentelemetry.exporter.MetricsExportConfig;
import com.automq.opentelemetry.exporter.MetricsExporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
/**
* An adapter class that implements the MetricsExporter interface and uses S3MetricsExporter
* for actual metrics exporting functionality.
*/
public class S3MetricsExporterAdapter implements MetricsExporter {
private static final Logger LOGGER = LoggerFactory.getLogger(S3MetricsExporterAdapter.class);
private final MetricsExportConfig metricsExportConfig;
/**
* Creates a new S3MetricsExporterAdapter.
*
* @param metricsExportConfig The configuration for the S3 metrics exporter.
*/
public S3MetricsExporterAdapter(MetricsExportConfig metricsExportConfig) {
this.metricsExportConfig = metricsExportConfig;
LOGGER.info("S3MetricsExporterAdapter initialized with labels :{}", metricsExportConfig.baseLabels());
}
@Override
public MetricReader asMetricReader() {
// Create and start the S3MetricsExporter
S3MetricsExporter s3MetricsExporter = new S3MetricsExporter(metricsExportConfig);
s3MetricsExporter.start();
// Create and return the periodic metric reader
return PeriodicMetricReader.builder(s3MetricsExporter)
.setInterval(Duration.ofMillis(metricsExportConfig.intervalMs()))
.build();
}
}

View File

@ -1,15 +1,23 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 kafka.log.stream.s3.telemetry.otel;
package com.automq.opentelemetry.yammer;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.Timer;

View File

@ -1,15 +1,23 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 kafka.log.stream.s3.telemetry.otel;
package com.automq.opentelemetry.yammer;
import com.yammer.metrics.core.MetricName;

View File

@ -1,17 +1,24 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 kafka.log.stream.s3.telemetry.otel;
package com.automq.opentelemetry.yammer;
import kafka.autobalancer.metricsreporter.metric.MetricsUtils;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Gauge;
@ -24,16 +31,54 @@ import com.yammer.metrics.core.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.Meter;
import scala.UninitializedFieldError;
public class OTelMetricsProcessor implements MetricProcessor<Void> {
private static final Logger LOGGER = LoggerFactory.getLogger(OTelMetricsProcessor.class);
/**
* A metrics processor that bridges Yammer metrics to OpenTelemetry metrics.
*
* <p>This processor specifically handles Histogram and Timer metrics from the Yammer metrics
* library and converts them to OpenTelemetry gauge metrics that track delta mean values.
* It implements the Yammer {@link MetricProcessor} interface to process metrics and creates
* corresponding OpenTelemetry metrics with proper attributes derived from the metric scope.
*
* <p>The processor:
* <ul>
* <li>Converts Yammer Histogram and Timer metrics to OpenTelemetry gauges</li>
* <li>Calculates delta mean values using {@link DeltaHistogram}</li>
* <li>Parses metric scopes to extract attributes for OpenTelemetry metrics</li>
* <li>Maintains a registry of processed metrics for lifecycle management</li>
* <li>Supports metric removal when metrics are no longer needed</li>
* </ul>
*
* <p>Supported metric types:
* <ul>
* <li>{@link Histogram} - Converted to delta mean gauge</li>
* <li>{@link Timer} - Converted to delta mean gauge</li>
* </ul>
*
* <p>Unsupported metric types (will throw {@link UnsupportedOperationException}):
* <ul>
* <li>{@link Counter}</li>
* <li>{@link Gauge}</li>
* <li>{@link Metered}</li>
* </ul>
*
* <p>Thread Safety: This class is thread-safe and uses concurrent data structures
* to handle metrics registration and removal from multiple threads.
*
* @see MetricProcessor
* @see DeltaHistogram
* @see OTelMetricUtils
*/
public class YammerMetricsProcessor implements MetricProcessor<Void> {
private static final Logger LOGGER = LoggerFactory.getLogger(YammerMetricsProcessor.class);
private final Map<String, Map<MetricName, MetricWrapper>> metrics = new ConcurrentHashMap<>();
private Meter meter = null;
@ -63,9 +108,9 @@ public class OTelMetricsProcessor implements MetricProcessor<Void> {
private void processDeltaHistogramMetric(MetricName name, DeltaHistogram deltaHistogram) {
if (meter == null) {
throw new UninitializedFieldError("Meter is not initialized");
throw new IllegalStateException("Meter is not initialized");
}
Map<String, String> tags = MetricsUtils.yammerMetricScopeToTags(name.getScope());
Map<String, String> tags = yammerMetricScopeToTags(name.getScope());
AttributesBuilder attrBuilder = Attributes.builder();
if (tags != null) {
String value = tags.remove(OTelMetricUtils.REQUEST_TAG_KEY);
@ -108,6 +153,29 @@ public class OTelMetricsProcessor implements MetricProcessor<Void> {
});
}
/**
* Convert a yammer metrics scope to a tags map.
*
* @param scope Scope of the Yammer metric.
* @return Empty map for {@code null} scope, {@code null} for scope with keys without a matching value (i.e. unacceptable
* scope) (see <a href="https://github.com/linkedin/cruise-control/issues/1296">...</a>), parsed tags otherwise.
*/
public static Map<String, String> yammerMetricScopeToTags(String scope) {
if (scope != null) {
String[] kv = scope.split("\\.");
if (kv.length % 2 != 0) {
return null;
}
Map<String, String> tags = new HashMap<>();
for (int i = 0; i < kv.length; i += 2) {
tags.put(kv[i], kv[i + 1]);
}
return tags;
} else {
return Collections.emptyMap();
}
}
static class MetricWrapper {
private final Attributes attr;
private final DeltaHistogram deltaHistogram;

View File

@ -0,0 +1,93 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.opentelemetry.yammer;
import com.yammer.metrics.core.Metric;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.core.MetricsRegistryListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import io.opentelemetry.api.metrics.Meter;
/**
* A listener that bridges Yammer Histogram metrics to OpenTelemetry.
* It listens for new metrics added to a MetricsRegistry and creates corresponding
* OTel gauge metrics for mean and max values of histograms.
*/
public class YammerMetricsReporter implements MetricsRegistryListener, Closeable {
private static final Logger LOGGER = LoggerFactory.getLogger(YammerMetricsReporter.class);
private final MetricsRegistry metricsRegistry;
private final YammerMetricsProcessor metricsProcessor;
private volatile Meter meter;
public YammerMetricsReporter(MetricsRegistry metricsRegistry) {
this.metricsRegistry = metricsRegistry;
this.metricsProcessor = new YammerMetricsProcessor();
}
public void start(Meter meter) {
this.meter = meter;
this.metricsProcessor.init(meter);
metricsRegistry.addListener(this);
LOGGER.info("OTelHistogramReporter started");
}
@Override
public void onMetricAdded(MetricName name, Metric metric) {
if (OTelMetricUtils.isInterestedMetric(name)) {
if (this.meter == null) {
LOGGER.info("Not initialized yet, skipping metric: {}", name);
return;
}
try {
metric.processWith(this.metricsProcessor, name, null);
} catch (Throwable t) {
LOGGER.error("Failed to process metric: {}", name, t);
}
}
}
@Override
public void onMetricRemoved(MetricName name) {
try {
this.metricsProcessor.remove(name);
} catch (Throwable ignored) {
}
}
@Override
public void close() throws IOException {
try {
// Remove this reporter as a listener from the metrics registry
metricsRegistry.removeListener(this);
LOGGER.info("YammerMetricsReporter stopped and removed from metrics registry");
} catch (Exception e) {
LOGGER.error("Error while closing YammerMetricsReporter", e);
throw new IOException("Failed to close YammerMetricsReporter", e);
}
}
}

View File

@ -18,7 +18,8 @@ dependencies {
compileOnly libs.awsSdkAuth
implementation libs.reload4j
implementation libs.nettyBuffer
implementation libs.opentelemetrySdk
implementation project(':automq-metrics')
implementation project(':automq-log-uploader')
implementation libs.jacksonDatabind
implementation libs.jacksonYaml
implementation libs.commonLang
@ -65,4 +66,4 @@ jar {
manifest {
attributes 'Main-Class': 'com.automq.shell.AutoMQCLI'
}
}
}

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.commands.cluster;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.commands.cluster;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.commands.cluster;
@ -102,9 +110,11 @@ public class Deploy implements Callable<Integer> {
String globalAccessKey = null;
String globalSecretKey = null;
for (Env env : topo.getGlobal().getEnvs()) {
if ("KAFKA_S3_ACCESS_KEY".equals(env.getName())) {
if ("KAFKA_S3_ACCESS_KEY".equals(env.getName()) ||
"AWS_ACCESS_KEY_ID".equals(env.getName())) {
globalAccessKey = env.getValue();
} else if ("KAFKA_S3_SECRET_KEY".equals(env.getName())) {
} else if ("KAFKA_S3_SECRET_KEY".equals(env.getName()) ||
"AWS_SECRET_ACCESS_KEY".equals(env.getName())) {
globalSecretKey = env.getValue();
}
}
@ -159,6 +169,7 @@ public class Deploy implements Callable<Integer> {
sb.append("--override cluster.id=").append(topo.getGlobal().getClusterId()).append(" ");
sb.append("--override node.id=").append(node.getNodeId()).append(" ");
sb.append("--override controller.quorum.voters=").append(getQuorumVoters(topo)).append(" ");
sb.append("--override controller.quorum.bootstrap.servers=").append(getBootstrapServers(topo)).append(" ");
sb.append("--override advertised.listeners=").append("PLAINTEXT://").append(node.getHost()).append(":9092").append(" ");
}
@ -181,4 +192,14 @@ public class Deploy implements Callable<Integer> {
.map(node -> node.getNodeId() + "@" + node.getHost() + ":9093")
.collect(Collectors.joining(","));
}
private static String getBootstrapServers(ClusterTopology topo) {
List<Node> nodes = topo.getControllers();
if (!(nodes.size() == 1 || nodes.size() == 3)) {
throw new IllegalArgumentException("Only support 1 or 3 controllers");
}
return nodes.stream()
.map(node -> node.getHost() + ":9093")
.collect(Collectors.joining(","));
}
}

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.commands.cluster;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.constant;

View File

@ -1,27 +0,0 @@
/*
* Copyright 2024, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
*/
package com.automq.shell.log;
import com.automq.stream.s3.operator.ObjectStorage;
public interface S3LogConfig {
boolean isEnabled();
boolean isActiveController();
String clusterId();
int nodeId();
ObjectStorage objectStorage();
}

View File

@ -1,42 +0,0 @@
/*
* Copyright 2024, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
*/
package com.automq.shell.log;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.LoggingEvent;
public class S3RollingFileAppender extends RollingFileAppender {
private final LogUploader logUploader = LogUploader.getInstance();
@Override
protected void subAppend(LoggingEvent event) {
super.subAppend(event);
if (!closed) {
LogRecorder.LogEvent logEvent = new LogRecorder.LogEvent(
event.getTimeStamp(),
event.getLevel().toString(),
event.getLoggerName(),
event.getRenderedMessage(),
event.getThrowableStrRep());
try {
logEvent.validate();
} catch (IllegalArgumentException e) {
// Drop invalid log event
errorHandler.error("Failed to validate log event", e, 0);
return;
}
logUploader.append(logEvent);
}
}
}

View File

@ -1,120 +0,0 @@
/*
* Copyright 2024, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
*/
package com.automq.shell.metrics;
import org.apache.commons.lang3.StringUtils;
public class PrometheusUtils {
private static final String TOTAL_SUFFIX = "_total";
public static String getPrometheusUnit(String unit) {
if (unit.contains("{")) {
return "";
}
switch (unit) {
// Time
case "d":
return "days";
case "h":
return "hours";
case "min":
return "minutes";
case "s":
return "seconds";
case "ms":
return "milliseconds";
case "us":
return "microseconds";
case "ns":
return "nanoseconds";
// Bytes
case "By":
return "bytes";
case "KiBy":
return "kibibytes";
case "MiBy":
return "mebibytes";
case "GiBy":
return "gibibytes";
case "TiBy":
return "tibibytes";
case "KBy":
return "kilobytes";
case "MBy":
return "megabytes";
case "GBy":
return "gigabytes";
case "TBy":
return "terabytes";
// SI
case "m":
return "meters";
case "V":
return "volts";
case "A":
return "amperes";
case "J":
return "joules";
case "W":
return "watts";
case "g":
return "grams";
// Misc
case "Cel":
return "celsius";
case "Hz":
return "hertz";
case "1":
return "";
case "%":
return "percent";
default:
return unit;
}
}
public static String mapMetricsName(String name, String unit, boolean isCounter, boolean isGauge) {
// Replace "." into "_"
name = name.replaceAll("\\.", "_");
String prometheusUnit = getPrometheusUnit(unit);
boolean shouldAppendUnit = StringUtils.isNotBlank(prometheusUnit) && !name.contains(prometheusUnit);
// append prometheus unit if not null or empty.
// unit should be appended before type suffix
if (shouldAppendUnit) {
name = name + "_" + prometheusUnit;
}
// trim counter's _total suffix so the unit is placed before it.
if (isCounter && name.endsWith(TOTAL_SUFFIX)) {
name = name.substring(0, name.length() - TOTAL_SUFFIX.length());
}
// replace _total suffix, or add if it wasn't already present.
if (isCounter) {
name = name + TOTAL_SUFFIX;
}
// special case - gauge
if (unit.equals("1") && isGauge && !name.contains("ratio")) {
name = name + "_ratio";
}
return name;
}
public static String mapLabelName(String name) {
if (StringUtils.isBlank(name)) {
return "";
}
return name.replaceAll("\\.", "_");
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright 2024, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
*/
package com.automq.shell.metrics;
import com.automq.stream.s3.operator.ObjectStorage;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
public interface S3MetricsConfig {
String clusterId();
boolean isActiveController();
int nodeId();
ObjectStorage objectStorage();
List<Pair<String, String>> baseLabels();
}

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.model;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.model;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.model;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.model;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.model;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.model;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.model;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.stream;
@ -29,7 +37,6 @@ import org.apache.kafka.common.requests.s3.GetKVsRequest;
import org.apache.kafka.common.requests.s3.PutKVsRequest;
import org.apache.kafka.common.utils.Time;
import com.automq.shell.metrics.S3MetricsExporter;
import com.automq.stream.api.KeyValue;
import org.slf4j.Logger;
@ -40,7 +47,7 @@ import java.util.List;
import java.util.Objects;
public class ClientKVClient {
private static final Logger LOGGER = LoggerFactory.getLogger(S3MetricsExporter.class);
private static final Logger LOGGER = LoggerFactory.getLogger(ClientKVClient.class);
private final NetworkClient networkClient;
private final Node bootstrapServer;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.stream;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.util;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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 com.automq.shell.util;

View File

@ -0,0 +1,69 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.shell.util;
import com.automq.stream.s3.ByteBufAlloc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import io.netty.buffer.ByteBuf;
public class Utils {
public static ByteBuf compress(ByteBuf input) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
byte[] buffer = new byte[input.readableBytes()];
input.readBytes(buffer);
gzipOutputStream.write(buffer);
gzipOutputStream.close();
ByteBuf compressed = ByteBufAlloc.byteBuffer(byteArrayOutputStream.size());
compressed.writeBytes(byteArrayOutputStream.toByteArray());
return compressed;
}
public static ByteBuf decompress(ByteBuf input) throws IOException {
byte[] compressedData = new byte[input.readableBytes()];
input.readBytes(compressedData);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData);
GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = gzipInputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
gzipInputStream.close();
byteArrayOutputStream.close();
byte[] uncompressedData = byteArrayOutputStream.toByteArray();
ByteBuf output = ByteBufAlloc.byteBuffer(uncompressedData.length);
output.writeBytes(uncompressedData);
return output;
}
}

View File

@ -9,10 +9,12 @@ global:
config: |
s3.data.buckets=0@s3://xxx_bucket?region=us-east-1
s3.ops.buckets=1@s3://xxx_bucket?region=us-east-1
s3.wal.path=0@s3://xxx_bucket?region=us-east-1
log.dirs=/root/kraft-logs
envs:
- name: KAFKA_S3_ACCESS_KEY
- name: AWS_ACCESS_KEY_ID
value: 'xxxxx'
- name: KAFKA_S3_SECRET_KEY
- name: AWS_SECRET_ACCESS_KEY
value: 'xxxxx'
controllers:
# The controllers default are combined nodes which roles are controller and broker.

View File

@ -0,0 +1,50 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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 com.automq.shell.util;
import com.automq.stream.s3.ByteBufAlloc;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import io.netty.buffer.ByteBuf;
@Timeout(60)
@Tag("S3Unit")
public class UtilsTest {
@Test
public void testCompression() {
String testStr = "This is a test string";
ByteBuf input = ByteBufAlloc.byteBuffer(testStr.length());
input.writeBytes(testStr.getBytes());
try {
ByteBuf compressed = Utils.compress(input);
ByteBuf decompressed = Utils.decompress(compressed);
String decompressedStr = decompressed.toString(io.netty.util.CharsetUtil.UTF_8);
System.out.printf("Original: %s, Decompressed: %s\n", testStr, decompressedStr);
Assertions.assertEquals(testStr, decompressedStr);
} catch (Exception e) {
Assertions.fail("Exception occurred during compression/decompression: " + e.getMessage());
}
}
}

View File

@ -23,4 +23,10 @@ fi
if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
export KAFKA_HEAP_OPTS="-Xmx1024M"
fi
# Add additional help info for the new parameter (this won't be displayed directly but documents the change)
# --consumers-during-catchup: Percentage of consumers to activate during catch-up read (0-100, default: 100)
# This allows controlling what percentage of consumer groups are activated during catch-up
# reading to better simulate real-world scenarios where only a subset of consumers
# experience catch-up reads at the same time.
exec "$(dirname "$0")/kafka-run-class.sh" -name kafkaClient -loggc org.apache.kafka.tools.automq.PerfCommand "$@"

View File

@ -42,4 +42,5 @@ case $COMMAND in
;;
esac
export KAFKA_CONNECT_MODE=true
exec $(dirname $0)/kafka-run-class.sh $EXTRA_ARGS org.apache.kafka.connect.cli.ConnectDistributed "$@"

View File

@ -42,4 +42,5 @@ case $COMMAND in
;;
esac
export KAFKA_CONNECT_MODE=true
exec $(dirname $0)/kafka-run-class.sh $EXTRA_ARGS org.apache.kafka.connect.cli.ConnectStandalone "$@"

View File

@ -40,7 +40,23 @@ should_include_file() {
fi
file=$1
if [ -z "$(echo "$file" | grep -E "$regex")" ] ; then
return 0
# If Connect mode is enabled, apply additional filtering
if [ "$KAFKA_CONNECT_MODE" = "true" ]; then
# Skip if file doesn't exist
[ ! -f "$file" ] && return 1
# Exclude heavy dependencies that Connect doesn't need
case "$file" in
*hadoop*) return 1 ;;
*hive*) return 1 ;;
*iceberg*) return 1 ;;
*avro*) return 1 ;;
*parquet*) return 1 ;;
*) return 0 ;;
esac
else
return 0
fi
else
return 1
fi

View File

@ -47,7 +47,7 @@ if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
fi
if [ "x$KAFKA_OPTS" = "x" ]; then
export KAFKA_OPTS="-Dio.netty.allocator.maxOrder=11"
export KAFKA_OPTS="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -Dio.netty.allocator.maxOrder=11"
fi
EXTRA_ARGS=${EXTRA_ARGS-'-name kafkaServer -loggc'}

View File

@ -44,7 +44,9 @@ plugins {
// be dropped from gradle/resources/dependencycheck-suppressions.xml
id "com.github.spotbugs" version '5.1.3' apply false
id 'org.scoverage' version '8.0.3' apply false
id 'io.github.goooler.shadow' version '8.1.3' apply false
// Updating the shadow plugin version to 8.1.1 causes issue with signing and publishing the shadowed
// artifacts - see https://github.com/johnrengelman/shadow/issues/901
id 'com.github.johnrengelman.shadow' version '8.1.0' apply false
// Spotless 6.13.0 has issue with Java 21 (see https://github.com/diffplug/spotless/pull/1920), and Spotless 6.14.0+ requires JRE 11
// We are going to drop JDK8 support. Hence, the spotless is upgrade to newest version and be applied only if the build env is compatible with JDK 11.
// spotless 6.15.0+ has issue in runtime with JDK8 even through we define it with `apply:false`. see https://github.com/diffplug/spotless/issues/2156 for more details
@ -53,7 +55,7 @@ plugins {
ext {
gradleVersion = versions.gradle
minJavaVersion = 11
minJavaVersion = 17
buildVersionFileName = "kafka-version.properties"
defaultMaxHeapSize = "2g"
@ -128,6 +130,9 @@ allprojects {
repositories {
mavenCentral()
maven {
url = uri("https://packages.confluent.io/maven/")
}
}
dependencyUpdates {
@ -147,6 +152,10 @@ allprojects {
}
configurations.all {
// Globally exclude commons-logging and logback to ensure a single logging implementation (reload4j)
exclude group: "commons-logging", module: "commons-logging"
exclude group: "ch.qos.logback", module: "logback-classic"
exclude group: "ch.qos.logback", module: "logback-core"
// zinc is the Scala incremental compiler, it has a configuration for its own dependencies
// that are unrelated to the project dependencies, we should not change them
if (name != "zinc") {
@ -162,8 +171,8 @@ allprojects {
// ZooKeeper (potentially older and containing CVEs)
libs.nettyHandler,
libs.nettyTransportNativeEpoll,
// be explicit about the reload4j version instead of relying on the transitive versions
libs.reload4j
// be explicit about the reload4j version instead of relying on the transitive versions
libs.reload4j
)
}
}
@ -257,7 +266,10 @@ subprojects {
options.compilerArgs << "-Xlint:-rawtypes"
options.compilerArgs << "-Xlint:-serial"
options.compilerArgs << "-Xlint:-try"
options.compilerArgs << "-Werror"
// AutoMQ inject start
// TODO: remove me, when upgrade to 4.x
// options.compilerArgs << "-Werror"
// AutoMQ inject start
// --release is the recommended way to select the target release, but it's only supported in Java 9 so we also
// set --source and --target via `sourceCompatibility` and `targetCompatibility` a couple of lines below
@ -295,7 +307,7 @@ subprojects {
if (!shouldPublishWithShadow) {
from components.java
} else {
apply plugin: 'io.github.goooler.shadow'
apply plugin: 'com.github.johnrengelman.shadow'
project.shadow.component(mavenJava)
// Fix for avoiding inclusion of runtime dependencies marked as 'shadow' in MANIFEST Class-Path.
@ -728,7 +740,7 @@ subprojects {
jacoco {
toolVersion = versions.jacoco
}
jacocoTestReport {
dependsOn tasks.test
sourceSets sourceSets.main
@ -752,8 +764,8 @@ subprojects {
skipProjects = [ ":jmh-benchmarks", ":trogdor" ]
skipConfigurations = [ "zinc" ]
}
// the task `removeUnusedImports` is implemented by google-java-format,
// and unfortunately the google-java-format version used by spotless 6.14.0 can't work with JDK 21.
// the task `removeUnusedImports` is implemented by google-java-format,
// and unfortunately the google-java-format version used by spotless 6.14.0 can't work with JDK 21.
// Hence, we apply spotless tasks only if the env is either JDK11 or JDK17
if ((JavaVersion.current().isJava11() || (JavaVersion.current() == JavaVersion.VERSION_17))) {
apply plugin: 'com.diffplug.spotless'
@ -828,6 +840,13 @@ tasks.create(name: "jarConnect", dependsOn: connectPkgs.collect { it + ":jar" })
tasks.create(name: "testConnect", dependsOn: connectPkgs.collect { it + ":test" }) {}
// OpenTelemetry related tasks
tasks.create(name: "jarOpenTelemetry", dependsOn: ":opentelemetry:jar") {}
tasks.create(name: "testOpenTelemetry", dependsOn: ":opentelemetry:test") {}
tasks.create(name: "buildOpenTelemetry", dependsOn: [":opentelemetry:jar", ":opentelemetry:test"]) {}
project(':server') {
base {
archivesName = "kafka-server"
@ -835,6 +854,7 @@ project(':server') {
dependencies {
implementation project(':clients')
implementation project(':metadata')
implementation project(':server-common')
implementation project(':storage')
implementation project(':group-coordinator')
@ -928,6 +948,8 @@ project(':core') {
implementation project(':storage')
implementation project(':server')
implementation project(':automq-shell')
implementation project(':automq-metrics')
implementation project(':automq-log-uploader')
implementation libs.argparse4j
implementation libs.commonsValidator
@ -944,6 +966,7 @@ project(':core') {
implementation libs.scalaReflect
implementation libs.scalaLogging
implementation libs.slf4jApi
implementation libs.commonsIo // ZooKeeper dependency. Do not use, this is going away.
implementation(libs.zookeeper) {
// Dropwizard Metrics are required by ZooKeeper as of v3.6.0,
// but the library should *not* be used in Kafka code
@ -965,17 +988,77 @@ project(':core') {
implementation libs.guava
implementation libs.slf4jBridge
implementation libs.slf4jReload4j
// The `jcl-over-slf4j` library is used to redirect JCL logging to SLF4J.
implementation libs.jclOverSlf4j
implementation libs.opentelemetryJava8
implementation libs.opentelemetryOshi
implementation libs.opentelemetrySdk
implementation libs.opentelemetrySdkMetrics
implementation libs.opentelemetryExporterLogging
implementation libs.opentelemetryExporterProm
implementation libs.opentelemetryExporterOTLP
implementation libs.opentelemetryJmx
implementation libs.awsSdkAuth
// table topic start
implementation ("org.apache.avro:avro:${versions.avro}")
implementation ("org.apache.avro:avro-protobuf:${versions.avro}")
implementation('com.google.protobuf:protobuf-java:3.25.5')
implementation ("org.apache.iceberg:iceberg-core:${versions.iceberg}")
implementation ("org.apache.iceberg:iceberg-api:${versions.iceberg}")
implementation ("org.apache.iceberg:iceberg-data:${versions.iceberg}")
implementation ("org.apache.iceberg:iceberg-parquet:${versions.iceberg}")
implementation ("org.apache.iceberg:iceberg-common:${versions.iceberg}")
implementation ("org.apache.iceberg:iceberg-aws:${versions.iceberg}")
implementation ("org.apache.iceberg:iceberg-nessie:${versions.iceberg}")
implementation ("software.amazon.awssdk:glue:${versions.awsSdk}")
implementation ("software.amazon.awssdk:s3tables:${versions.awsSdk}")
implementation 'software.amazon.s3tables:s3-tables-catalog-for-iceberg:0.1.0'
implementation ('org.apache.hadoop:hadoop-common:3.4.1') {
exclude group: 'org.eclipse.jetty', module: '*'
exclude group: 'com.sun.jersey', module: '*'
}
// for hadoop common
implementation ("org.eclipse.jetty:jetty-webapp:${versions.jetty}")
implementation (libs.kafkaAvroSerializer) {
exclude group: 'org.apache.kafka', module: 'kafka-clients'
}
// > hive ext start
implementation 'org.apache.iceberg:iceberg-hive-metastore:1.6.1'
implementation('org.apache.hive:hive-metastore:3.1.3') {
// Remove useless dependencies (copy from iceberg-kafka-connect)
exclude group: "org.apache.avro", module: "avro"
exclude group: "org.slf4j", module: "slf4j-log4j12"
exclude group: "org.pentaho" // missing dependency
exclude group: "org.apache.hbase"
exclude group: "org.apache.logging.log4j"
exclude group: "co.cask.tephra"
exclude group: "com.google.code.findbugs", module: "jsr305"
exclude group: "org.eclipse.jetty.aggregate", module: "jetty-all"
exclude group: "org.eclipse.jetty.orbit", module: "javax.servlet"
exclude group: "org.apache.parquet", module: "parquet-hadoop-bundle"
exclude group: "com.tdunning", module: "json"
exclude group: "javax.transaction", module: "transaction-api"
exclude group: "com.zaxxer", module: "HikariCP"
exclude group: "org.apache.hadoop", module: "hadoop-yarn-server-common"
exclude group: "org.apache.hadoop", module: "hadoop-yarn-server-applicationhistoryservice"
exclude group: "org.apache.hadoop", module: "hadoop-yarn-server-resourcemanager"
exclude group: "org.apache.hadoop", module: "hadoop-yarn-server-web-proxy"
exclude group: "org.apache.hive", module: "hive-service-rpc"
exclude group: "com.github.joshelser", module: "dropwizard-metrics-hadoop-metrics2-reporter"
}
implementation ('org.apache.hadoop:hadoop-mapreduce-client-core:3.4.1') {
exclude group: 'com.sun.jersey', module: '*'
exclude group: 'com.sun.jersey.contribs', module: '*'
exclude group: 'com.github.pjfanning', module: 'jersey-json'
}
// > hive ext end
// > Protobuf ext start
// Wire Runtime for schema handling
implementation ("com.squareup.wire:wire-schema:${versions.wire}")
implementation ("com.squareup.wire:wire-runtime:${versions.wire}")
implementation 'com.google.api.grpc:proto-google-common-protos:2.52.0'
// > Protobuf ext end
// table topic end
implementation(libs.oshi) {
exclude group: 'org.slf4j', module: '*'
}
@ -990,6 +1073,7 @@ project(':core') {
testImplementation project(':storage:storage-api').sourceSets.test.output
testImplementation project(':server').sourceSets.test.output
testImplementation libs.bcpkix
testImplementation libs.mockitoJunitJupiter // supports MockitoExtension
testImplementation libs.mockitoCore
testImplementation libs.guava
testImplementation(libs.apacheda) {
@ -1160,7 +1244,6 @@ project(':core') {
from(project.file("$rootDir/docker/docker-compose.yaml")) { into "docker/" }
from(project.file("$rootDir/docker/telemetry")) { into "docker/telemetry/" }
from(project.file("$rootDir/LICENSE")) { into "" }
from(project.file("$rootDir/LICENSE.S3Stream")) { into "" }
from "$rootDir/NOTICE-binary" rename {String filename -> filename.replace("-binary", "")}
from(configurations.runtimeClasspath) { into("libs/") }
from(configurations.archives.artifacts.files) { into("libs/") }
@ -1171,6 +1254,10 @@ project(':core') {
from(project(':trogdor').configurations.runtimeClasspath) { into("libs/") }
from(project(':automq-shell').jar) { into("libs/") }
from(project(':automq-shell').configurations.runtimeClasspath) { into("libs/") }
from(project(':automq-metrics').jar) { into("libs/") }
from(project(':automq-metrics').configurations.runtimeClasspath) { into("libs/") }
from(project(':automq-log-uploader').jar) { into("libs/") }
from(project(':automq-log-uploader').configurations.runtimeClasspath) { into("libs/") }
from(project(':shell').jar) { into("libs/") }
from(project(':shell').configurations.runtimeClasspath) { into("libs/") }
from(project(':connect:api').jar) { into("libs/") }
@ -1201,6 +1288,38 @@ project(':core') {
from(project(':tools:tools-api').configurations.runtimeClasspath) { into("libs/") }
duplicatesStrategy 'exclude'
}
// AutoMQ inject start
tasks.create(name: "releaseE2ETar", dependsOn: [configurations.archives.artifacts, 'copyDependantTestLibs'], type: Tar) {
def prefix = project.findProperty('prefix') ?: ''
archiveBaseName = "${prefix}kafka"
into "${prefix}kafka-${archiveVersion.get()}"
compression = Compression.GZIP
from(project.file("$rootDir/bin")) { into "bin/" }
from(project.file("$rootDir/config")) { into "config/" }
from(project.file("$rootDir/licenses")) { into "licenses/" }
from(project.file("$rootDir/docker/docker-compose.yaml")) { into "docker/" }
from(project.file("$rootDir/docker/telemetry")) { into "docker/telemetry/" }
from(project.file("$rootDir/LICENSE")) { into "" }
from "$rootDir/NOTICE-binary" rename {String filename -> filename.replace("-binary", "")}
from(configurations.runtimeClasspath) { into("libs/") }
from(configurations.archives.artifacts.files) { into("libs/") }
from(project.siteDocsTar) { into("site-docs/") }
// Include main and test jars from all subprojects
rootProject.subprojects.each { subproject ->
if (subproject.tasks.findByName('jar')) {
from(subproject.tasks.named('jar')) { into('libs/') }
}
if (subproject.tasks.findByName('testJar')) {
from(subproject.tasks.named('testJar')) { into('libs/') }
}
from(subproject.configurations.runtimeClasspath) { into('libs/') }
}
duplicatesStrategy 'exclude'
}
// AutoMQ inject end
jar {
dependsOn('copyDependantLibs')
@ -1220,7 +1339,7 @@ project(':core') {
//By default gradle does not handle test dependencies between the sub-projects
//This line is to include clients project test jar to dependant-testlibs
from (project(':clients').testJar ) { "$buildDir/dependant-testlibs" }
// log4j-appender is not in core dependencies,
// log4j-appender is not in core dependencies,
// so we add it to dependant-testlibs to avoid ClassNotFoundException in running kafka_log4j_appender.py
from (project(':log4j-appender').jar ) { "$buildDir/dependant-testlibs" }
duplicatesStrategy 'exclude'
@ -1253,6 +1372,7 @@ project(':core') {
}
}
project(':metadata') {
base {
archivesName = "kafka-metadata"
@ -1275,6 +1395,7 @@ project(':metadata') {
implementation libs.guava
implementation libs.awsSdkAuth
implementation project(':s3stream')
implementation ("org.apache.avro:avro:${versions.avro}")
implementation libs.jacksonDatabind
implementation libs.jacksonJDK8Datatypes
@ -1480,7 +1601,7 @@ project(':transaction-coordinator') {
implementation project(':clients')
generator project(':generator')
}
sourceSets {
main {
java {
@ -1571,6 +1692,7 @@ project(':clients') {
implementation libs.snappy
implementation libs.slf4jApi
implementation libs.opentelemetryProto
implementation libs.protobuf
// libraries which should be added as runtime dependencies in generated pom.xml should be defined here:
shadowed libs.zstd
@ -1755,6 +1877,7 @@ project(':raft') {
testImplementation libs.junitJupiter
testImplementation libs.mockitoCore
testImplementation libs.jqwik
testImplementation libs.hamcrest
testRuntimeOnly libs.slf4jReload4j
testRuntimeOnly libs.junitPlatformLanucher
@ -1845,7 +1968,12 @@ project(':server-common') {
implementation libs.jacksonDatabind
implementation libs.pcollections
implementation libs.opentelemetrySdk
// AutoMQ inject start
implementation project(':s3stream')
implementation libs.commonLang
// AutoMQ inject end
testImplementation project(':clients')
testImplementation project(':clients').sourceSets.test.output
@ -2140,11 +2268,12 @@ project(':s3stream') {
implementation 'commons-codec:commons-codec:1.17.0'
implementation 'org.hdrhistogram:HdrHistogram:2.2.2'
implementation 'software.amazon.awssdk.crt:aws-crt:0.30.8'
implementation 'com.ibm.async:asyncutil:0.1.0'
testImplementation 'org.slf4j:slf4j-simple:2.0.9'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
testImplementation 'org.mockito:mockito-core:5.5.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.5.0'
testImplementation 'org.slf4j:slf4j-simple:1.7.36'
testImplementation libs.junitJupiter
testImplementation libs.mockitoCore
testImplementation libs.mockitoJunitJupiter // supports MockitoExtension
testImplementation 'org.awaitility:awaitility:4.2.1'
}
@ -2211,27 +2340,115 @@ project(':tools:tools-api') {
}
}
project(':automq-metrics') {
archivesBaseName = "automq-metrics"
checkstyle {
configProperties = checkstyleConfigProperties("import-control-server.xml")
}
dependencies {
// OpenTelemetry core dependencies
api libs.opentelemetryJava8
api libs.opentelemetryOshi
api libs.opentelemetrySdk
api libs.opentelemetrySdkMetrics
api libs.opentelemetryExporterLogging
api libs.opentelemetryExporterProm
api libs.opentelemetryExporterOTLP
api libs.opentelemetryJmx
// Logging dependencies
api libs.slf4jApi
api libs.slf4jBridge // SLF4J Bridge
api libs.reload4j
api libs.commonLang
// Yammer metrics (for integration)
api 'com.yammer.metrics:metrics-core:2.2.0'
implementation(project(':s3stream')) {
exclude(group: 'io.opentelemetry', module: '*')
exclude(group: 'io.opentelemetry.instrumentation', module: '*')
exclude(group: 'io.opentelemetry.proto', module: '*')
exclude(group: 'io.netty', module: 'netty-tcnative-boringssl-static')
exclude(group: 'com.github.jnr', module: '*')
exclude(group: 'org.aspectj', module: '*')
exclude(group: 'net.java.dev.jna', module: '*')
exclude(group: 'net.sourceforge.argparse4j', module: '*')
exclude(group: 'com.bucket4j', module: '*')
exclude(group: 'com.yammer.metrics', module: '*')
exclude(group: 'com.github.spotbugs', module: '*')
exclude(group: 'org.apache.kafka.shaded', module: '*')
}
implementation libs.nettyBuffer
implementation libs.jacksonDatabind
implementation libs.guava
implementation project(':clients')
// Test dependencies
testImplementation libs.junitJupiter
testImplementation libs.mockitoCore
testImplementation libs.slf4jReload4j
testRuntimeOnly libs.junitPlatformLanucher
implementation('io.opentelemetry:opentelemetry-sdk:1.40.0')
implementation("io.opentelemetry.semconv:opentelemetry-semconv:1.25.0-alpha")
implementation("io.opentelemetry.instrumentation:opentelemetry-runtime-telemetry-java8:2.6.0-alpha")
implementation('com.google.protobuf:protobuf-java:3.25.5')
implementation('org.xerial.snappy:snappy-java:1.1.10.5')
}
clean.doFirst {
delete "$buildDir/kafka/"
}
javadoc {
enabled = false
}
}
project(':automq-log-uploader') {
archivesBaseName = "automq-log-uploader"
checkstyle {
configProperties = checkstyleConfigProperties("import-control-server.xml")
}
dependencies {
api project(':s3stream')
implementation project(':clients')
implementation libs.reload4j
implementation libs.slf4jApi
implementation libs.slf4jBridge
implementation libs.nettyBuffer
implementation libs.guava
implementation libs.commonLang
}
javadoc {
enabled = false
}
}
project(':tools') {
base {
archivesName = "kafka-tools"
}
dependencies {
implementation (project(':clients')){
exclude group: 'org.slf4j', module: '*'
}
implementation (project(':server-common')){
exclude group: 'org.slf4j', module: '*'
}
implementation (project(':log4j-appender')){
exclude group: 'org.slf4j', module: '*'
}
implementation project(':automq-shell')
implementation project(':clients')
implementation project(':metadata')
implementation project(':storage')
implementation project(':server')
implementation project(':server-common')
implementation project(':connect:runtime')
implementation project(':tools:tools-api')
implementation project(':transaction-coordinator')
implementation project(':group-coordinator')
implementation libs.argparse4j
implementation libs.jacksonDatabind
implementation libs.jacksonDataformatCsv
@ -2243,6 +2460,16 @@ project(':tools') {
implementation libs.hdrHistogram
implementation libs.spotbugsAnnotations
// AutoMQ inject start
implementation project(':automq-shell')
implementation libs.guava
implementation (libs.kafkaAvroSerializer) {
exclude group: 'org.apache.kafka', module: 'kafka-clients'
}
implementation libs.bucket4j
implementation libs.oshi
// AutoMQ inject end
// for SASL/OAUTHBEARER JWT validation
implementation (libs.jose4j){
exclude group: 'org.slf4j', module: '*'
@ -2279,7 +2506,7 @@ project(':tools') {
testImplementation project(':connect:runtime')
testImplementation project(':connect:runtime').sourceSets.test.output
testImplementation project(':storage:storage-api').sourceSets.main.output
testImplementation project(':group-coordinator')
testImplementation project(':storage').sourceSets.test.output
testImplementation libs.junitJupiter
testImplementation libs.mockitoCore
testImplementation libs.mockitoJunitJupiter // supports MockitoExtension
@ -2577,6 +2804,7 @@ project(':streams') {
':streams:upgrade-system-tests-35:test',
':streams:upgrade-system-tests-36:test',
':streams:upgrade-system-tests-37:test',
':streams:upgrade-system-tests-38:test',
':streams:examples:test'
]
)
@ -3076,9 +3304,24 @@ project(':streams:upgrade-system-tests-37') {
}
}
project(':streams:upgrade-system-tests-38') {
base {
archivesName = "kafka-streams-upgrade-system-tests-38"
}
dependencies {
testImplementation libs.kafkaStreams_38
testRuntimeOnly libs.junitJupiter
}
systemTestLibs {
dependsOn testJar
}
}
project(':jmh-benchmarks') {
apply plugin: 'io.github.goooler.shadow'
apply plugin: 'com.github.johnrengelman.shadow'
shadowJar {
archiveBaseName = 'kafka-jmh-benchmarks'
@ -3308,6 +3551,8 @@ project(':connect:runtime') {
api project(':clients')
api project(':connect:json')
api project(':connect:transforms')
api project(':automq-metrics')
api project(':automq-log-uploader')
implementation libs.slf4jApi
implementation libs.reload4j
@ -3316,6 +3561,7 @@ project(':connect:runtime') {
implementation libs.jacksonJaxrsJsonProvider
implementation libs.jerseyContainerServlet
implementation libs.jerseyHk2
implementation libs.jaxrsApi
implementation libs.jaxbApi // Jersey dependency that was available in the JDK before Java 9
implementation libs.activation // Jersey dependency that was available in the JDK before Java 9
implementation libs.jettyServer

62
chart/bitnami/README.md Normal file
View File

@ -0,0 +1,62 @@
# AutoMQ
[AutoMQ](https://www.automq.com/) is a cloud-native alternative to Kafka by decoupling durability to cloud storage services like S3. 10x Cost-Effective. No Cross-AZ Traffic Cost. Autoscale in seconds. Single-digit ms latency.
This Helm chart simplifies the deployment of AutoMQ into your Kubernetes cluster using the Software model.
## Prerequisites
### Install Helm chart
Install Helm chart and version v3.8.0+
[Helm chart quickstart](https://helm.sh/zh/docs/intro/quickstart/)
```shell
helm version
```
### Using the Bitnami Helm repository
AutoMQ is fully compatible with Bitnami's Helm Charts, so you can customize your AutoMQ Kubernetes cluster based on the relevant values.yaml of Bitnami.
[Bitnami Helm Charts](https://github.com/bitnami/charts)
## Quickstart
### Setup a Kubernetes Cluster
The quickest way to set up a Kubernetes cluster to install Bitnami Charts is by following the "Bitnami Get Started" guides for the different services:
[Get Started with Bitnami Charts using the Amazon Elastic Container Service for Kubernetes (EKS)](https://docs.bitnami.com/kubernetes/get-started-eks/)
### Installing the AutoMQ with Bitnami Chart
As an alternative to supplying the configuration parameters as arguments, you can create a supplemental YAML file containing your specific config parameters. Any parameters not specified in this file will default to those set in [values.yaml](values.yaml).
1. Create an empty `automq-values.yaml` file
2. Edit the file with your specific parameters:
You can refer to the [demo-values.yaml](/chart/bitnami/demo-values.yaml) based on the bitnami [values.yaml](https://github.com/bitnami/charts/blob/main/bitnami/kafka/values.yaml)
we provided for deploying AutoMQ on AWS across 3 Availability Zones using m7g.xlarge instances (4 vCPUs, 16GB Mem, 156MiB/s network bandwidth).
You need to replace the bucket configurations in the placeholders $, such as ops-bucket, data-bucket, region, endpoint, access-key/secret-key.
3. Install or upgrade the AutoMQ Helm chart using your custom yaml file:
we recommend using the `--version` [31.x.x (31.1.0 ~ 31.5.0)](https://artifacthub.io/packages/helm/bitnami/kafka) bitnami helm chart while installing AutoMQ.
```shell
helm install automq-release oci://registry-1.docker.io/bitnamicharts/kafka -f demo-values.yaml --version 31.5.0 --namespace automq --create-namespace
```
### Upgrading
To upgrade the deployment:
```shell
helm repo update
helm upgrade automq-release oci://registry-1.docker.io/bitnamicharts/kafka -f demo-values.yaml --version 31.5.0 --namespace automq --create-namespace
```
### Uninstalling the Chart
To uninstall/delete the deployment:
```shell
helm uninstall automq-release --namespace automq
```
This command removes all the Kubernetes components associated with the chart and deletes the release.

View File

@ -0,0 +1,141 @@
global:
security:
allowInsecureImages: true
image:
registry: automqinc
repository: automq
tag: 1.6.1-rc0-bitnami
pullPolicy: Always
extraEnvVars:
- name: AWS_ACCESS_KEY_ID
value: "${access-key}"
- name: AWS_SECRET_ACCESS_KEY
value: "${secret-key}"
controller:
replicaCount: 3
resources:
requests:
cpu: "3000m"
memory: "12Gi"
limits:
cpu: "4000m"
memory: "16Gi"
heapOpts: -Xmx6g -Xms6g -XX:MaxDirectMemorySize=6g -XX:MetaspaceSize=96m
extraConfig: |
elasticstream.enable=true
autobalancer.client.auth.sasl.mechanism=PLAIN
autobalancer.client.auth.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="inter_broker_user" password="interbroker-password-placeholder" user_inter_broker_user="interbroker-password-placeholder";
autobalancer.client.auth.security.protocol=SASL_PLAINTEXT
autobalancer.client.listener.name=INTERNAL
s3.wal.cache.size=2147483648
s3.block.cache.size=1073741824
s3.stream.allocator.policy=POOLED_DIRECT
s3.network.baseline.bandwidth=245366784
# Replace the following with your bucket config
s3.ops.buckets=1@s3://${ops-bucket}?region=${region}&endpoint=${endpoint}
s3.data.buckets=0@s3://${data-bucket}?region=${region}&endpoint=${endpoint}
s3.wal.path=0@s3://${data-bucket}?region=${region}&endpoint=${endpoint}
automq.zonerouter.channels=0@s3://${data-bucket}?region=${region}&endpoint=${endpoint}
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/instance
operator: In
# your helm release name
values:
- automq-release
- key: app.kubernetes.io/component
operator: In
values:
- controller-eligible
- broker
topologyKey: kubernetes.io/hostname
# --- nodeAffinity recommended ---
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: "${your-node-label-key}"
# operator: In
# values:
# - "${your-node-label-value}"
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/component: controller-eligible
tolerations:
- key: "dedicated"
operator: "Equal"
value: "automq"
effect: "NoSchedule"
persistence:
size: 20Gi
broker:
replicaCount: 3
resources:
requests:
cpu: "3000m"
memory: "12Gi"
limits:
cpu: "4000m"
memory: "16Gi"
heapOpts: -Xmx6g -Xms6g -XX:MaxDirectMemorySize=6g -XX:MetaspaceSize=96m
extraConfig: |
elasticstream.enable=true
autobalancer.client.auth.sasl.mechanism=PLAIN
autobalancer.client.auth.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="inter_broker_user" password="interbroker-password-placeholder" user_inter_broker_user="interbroker-password-placeholder";
autobalancer.client.auth.security.protocol=SASL_PLAINTEXT
autobalancer.client.listener.name=INTERNAL
s3.wal.cache.size=2147483648
s3.block.cache.size=1073741824
s3.stream.allocator.policy=POOLED_DIRECT
s3.network.baseline.bandwidth=245366784
# Replace the following with your bucket config
s3.ops.buckets=1@s3://${ops-bucket}?region=${region}&endpoint=${endpoint}
s3.data.buckets=0@s3://${data-bucket}?region=${region}&endpoint=${endpoint}
s3.wal.path=0@s3://${data-bucket}?region=${region}&endpoint=${endpoint}
automq.zonerouter.channels=0@s3://${data-bucket}?region=${region}&endpoint=${endpoint}
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/instance
operator: In
# your helm release name
values:
- automq-release
- key: app.kubernetes.io/component
operator: In
values:
- controller-eligible
- broker
topologyKey: kubernetes.io/hostname
# --- nodeAffinity recommended ---
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: "${your-node-label-key}"
# operator: In
# values:
# - "${your-node-label-value}"
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/component: broker
tolerations:
- key: "dedicated"
operator: "Equal"
value: "automq"
effect: "NoSchedule"
brokerRackAssignment: aws-az

View File

@ -182,6 +182,10 @@
<subpackage name="migration">
<allow pkg="org.apache.kafka.controller" />
</subpackage>
<subpackage name="storage">
<allow pkg="org.apache.kafka.common.internals" />
<allow pkg="org.apache.kafka.snapshot" />
</subpackage>
<subpackage name="util">
<allow class="org.apache.kafka.common.compress.Compression" exact-match="true" />
</subpackage>

View File

@ -80,6 +80,8 @@
<allow pkg="org.apache.kafka.raft" />
<subpackage name="server">
<allow pkg="org.apache.kafka.server" />
<allow pkg="org.apache.kafka.image" />
<subpackage name="metrics">
<allow class="org.apache.kafka.server.authorizer.AuthorizableRequestContext" />
<allow pkg="org.apache.kafka.server.telemetry" />

View File

@ -83,6 +83,11 @@
<allow pkg="org.apache.kafka.coordinator.transaction"/>
</subpackage>
<subpackage name="storage.log">
<allow pkg="org.apache.kafka.server" />
<allow pkg="com.yammer.metrics" />
</subpackage>
<!-- START OF TIERED STORAGE INTEGRATION TEST IMPORT DEPENDENCIES -->
<subpackage name="tiered.storage">
<allow pkg="scala" />

View File

@ -49,6 +49,7 @@
<subpackage name="common">
<allow class="org.apache.kafka.clients.consumer.ConsumerRecord" exact-match="true" />
<allow class="org.apache.kafka.clients.NodeApiVersions" exact-match="true" />
<allow class="org.apache.kafka.common.message.ApiMessageType" exact-match="true" />
<disallow pkg="org.apache.kafka.clients" />
<allow pkg="org.apache.kafka.common" exact-match="true" />
@ -76,7 +77,10 @@
<allow pkg="net.jpountz.xxhash" />
<allow pkg="org.xerial.snappy" />
<allow pkg="org.apache.kafka.common.compress" />
<allow class="org.apache.kafka.common.record.CompressionType" exact-match="true" />
<allow class="org.apache.kafka.common.record.CompressionType" />
<allow class="org.apache.kafka.common.record.CompressionType.GZIP" />
<allow class="org.apache.kafka.common.record.CompressionType.LZ4" />
<allow class="org.apache.kafka.common.record.CompressionType.ZSTD" />
<allow class="org.apache.kafka.common.record.RecordBatch" exact-match="true" />
</subpackage>
@ -150,6 +154,7 @@
</subpackage>
<subpackage name="record">
<allow class="org.apache.kafka.common.config.ConfigDef.Range.between" exact-match="true" />
<allow pkg="org.apache.kafka.common.compress" />
<allow pkg="org.apache.kafka.common.header" />
<allow pkg="org.apache.kafka.common.record" />
@ -278,12 +283,16 @@
<subpackage name="tools">
<allow pkg="org.apache.kafka.common"/>
<allow pkg="org.apache.kafka.metadata.properties" />
<allow pkg="org.apache.kafka.network" />
<allow pkg="org.apache.kafka.server.util" />
<allow pkg="kafka.admin" />
<allow pkg="kafka.server" />
<allow pkg="org.apache.kafka.storage.internals" />
<allow pkg="org.apache.kafka.server.config" />
<allow pkg="org.apache.kafka.server.common" />
<allow pkg="org.apache.kafka.server.log.remote.metadata.storage" />
<allow pkg="org.apache.kafka.server.log.remote.storage" />
<allow pkg="org.apache.kafka.clients" />
<allow pkg="org.apache.kafka.clients.admin" />
<allow pkg="org.apache.kafka.clients.producer" />
@ -301,6 +310,7 @@
<allow pkg="kafka.utils" />
<allow pkg="scala.collection" />
<allow pkg="org.apache.kafka.coordinator.transaction" />
<allow pkg="org.apache.kafka.coordinator.group" />
<subpackage name="consumer">
<allow pkg="org.apache.kafka.tools"/>

View File

@ -39,7 +39,7 @@
<suppress checks="(NPathComplexity|ClassFanOutComplexity|CyclomaticComplexity|ClassDataAbstractionCoupling|FinalLocalVariable|LocalVariableName|MemberName|ParameterName|MethodLength|JavaNCSS|AvoidStarImport)"
files="core[\\/]src[\\/](generated|generated-test)[\\/].+.java$"/>
<suppress checks="NPathComplexity" files="(ClusterTestExtensions|KafkaApisBuilder|SharePartition).java"/>
<suppress checks="NPathComplexity|ClassFanOutComplexity|ClassDataAbstractionCoupling" files="(RemoteLogManager|RemoteLogManagerTest).java"/>
<suppress checks="NPathComplexity|ClassFanOutComplexity|ClassDataAbstractionCoupling|JavaNCSS" files="(RemoteLogManager|RemoteLogManagerTest).java"/>
<suppress checks="MethodLength" files="RemoteLogManager.java"/>
<suppress checks="ClassFanOutComplexity" files="RemoteLogManagerTest.java"/>
<suppress checks="MethodLength"
@ -190,11 +190,11 @@
<!-- Raft -->
<suppress checks="NPathComplexity"
files="RecordsIterator.java"/>
files="(DynamicVoter|RecordsIterator).java"/>
<!-- Streams -->
<suppress checks="ClassFanOutComplexity"
files="(KafkaStreams|KStreamImpl|KTableImpl|InternalTopologyBuilder|StreamsPartitionAssignor|StreamThread|IQv2StoreIntegrationTest|KStreamImplTest|RocksDBStore).java"/>
files="(KafkaStreams|KStreamImpl|KTableImpl|InternalTopologyBuilder|StreamsPartitionAssignor|StreamThread|IQv2StoreIntegrationTest|KStreamImplTest|RocksDBStore|StreamTask).java"/>
<suppress checks="MethodLength"
files="KTableImpl.java"/>
@ -326,7 +326,7 @@
<suppress checks="(ParameterNumber|ClassDataAbstractionCoupling)"
files="(QuorumController).java"/>
<suppress checks="(CyclomaticComplexity|NPathComplexity)"
files="(PartitionRegistration|PartitionChangeBuilder).java"/>
files="(PartitionRegistration|PartitionChangeBuilder|ScramParser).java"/>
<suppress checks="CyclomaticComplexity"
files="(ClientQuotasImage|KafkaEventQueue|MetadataDelta|QuorumController|ReplicationControlManager|KRaftMigrationDriver|ClusterControlManager|MetaPropertiesEnsemble).java"/>
<suppress checks="NPathComplexity"
@ -372,11 +372,12 @@
<suppress checks="CyclomaticComplexity"
files="(S3StreamsMetadataImage|S3StreamMetricsManager|BlockCache|StreamReader|S3MetricsExporter|PrometheusUtils).java"/>
<suppress checks="NPathComplexity"
files="(StreamControlManager|S3StreamsMetadataImage|CompactionManagerTest|S3StreamMetricsManager|CompactionManager|BlockCache|DefaultS3BlockCache|StreamReader|S3Utils|AnomalyDetector|Recreate|ForceClose|QuorumController).java"/>
files="(StreamControlManager|S3StreamsMetadataImage|CompactionManagerTest|S3StreamMetricsManager|CompactionManager|BlockCache|DefaultS3BlockCache|StreamReader|S3Utils|AnomalyDetector|Recreate|ForceClose|QuorumController|AbstractObjectStorage).java"/>
<suppress checks="MethodLength"
files="(S3StreamMetricsManager|BlockWALServiceTest).java"/>
<suppress id="dontUseSystemExit"
files="(BenchTool|S3Utils|AutoMQCLI).java"/>
<suppress checks="ClassDataAbstractionCoupling" files="(StreamControlManagerTest|ControllerStreamManager).java"/>
<suppress files="core[\/]src[\/]test[\/]java[\/]kafka[\/]automq[\/]table[\/]process[\/]proto[\/].*\.java$" checks=".*"/>
</suppressions>

View File

@ -18,9 +18,21 @@ package org.apache.kafka.clients.admin;
import org.apache.kafka.common.annotation.InterfaceStability;
import java.util.Optional;
/**
* Options for {@link Admin#addRaftVoter}.
*/
@InterfaceStability.Stable
public class AddRaftVoterOptions extends AbstractOptions<AddRaftVoterOptions> {
private Optional<String> clusterId = Optional.empty();
public AddRaftVoterOptions setClusterId(Optional<String> clusterId) {
this.clusterId = clusterId;
return this;
}
public Optional<String> clusterId() {
return clusterId;
}
}

View File

@ -1729,6 +1729,16 @@ public interface Admin extends AutoCloseable {
* @return {@link GetNodesResult}
*/
GetNodesResult getNodes(Collection<Integer> nodeIdList, GetNodesOptions options);
/**
* Update consumer group
*
* @param groupId group id
* @param groupSpec {@link UpdateGroupSpec}
* @param options {@link UpdateGroupOptions}
* @return {@link UpdateGroupResult}
*/
UpdateGroupResult updateGroup(String groupId, UpdateGroupSpec groupSpec, UpdateGroupOptions options);
// AutoMQ inject end
/**

View File

@ -314,5 +314,11 @@ public class ForwardingAdmin implements Admin {
public GetNodesResult getNodes(Collection<Integer> nodeIdList, GetNodesOptions options) {
return delegate.getNodes(nodeIdList, options);
}
@Override
public UpdateGroupResult updateGroup(String groupId, UpdateGroupSpec groupSpec, UpdateGroupOptions options) {
return delegate.updateGroup(groupId, groupSpec, options);
}
// AutoMQ inject end
}

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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.kafka.clients.admin;

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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.kafka.clients.admin;

View File

@ -56,6 +56,7 @@ import org.apache.kafka.clients.admin.internals.ListConsumerGroupOffsetsHandler;
import org.apache.kafka.clients.admin.internals.ListOffsetsHandler;
import org.apache.kafka.clients.admin.internals.ListTransactionsHandler;
import org.apache.kafka.clients.admin.internals.RemoveMembersFromConsumerGroupHandler;
import org.apache.kafka.clients.admin.internals.UpdateGroupHandler;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.clients.consumer.internals.ConsumerProtocol;
import org.apache.kafka.common.Cluster;
@ -241,6 +242,7 @@ import org.apache.kafka.common.requests.ListPartitionReassignmentsResponse;
import org.apache.kafka.common.requests.MetadataRequest;
import org.apache.kafka.common.requests.MetadataResponse;
import org.apache.kafka.common.requests.RemoveRaftVoterRequest;
import org.apache.kafka.common.requests.RemoveRaftVoterResponse;
import org.apache.kafka.common.requests.RenewDelegationTokenRequest;
import org.apache.kafka.common.requests.RenewDelegationTokenResponse;
import org.apache.kafka.common.requests.UnregisterBrokerRequest;
@ -1202,16 +1204,27 @@ public class KafkaAdminClient extends AdminClient {
long pollTimeout = Long.MAX_VALUE;
log.trace("Trying to choose nodes for {} at {}", pendingCalls, now);
Iterator<Call> pendingIter = pendingCalls.iterator();
while (pendingIter.hasNext()) {
Call call = pendingIter.next();
List<Call> toRemove = new ArrayList<>();
// Using pendingCalls.size() to get the list size before the for-loop to avoid infinite loop.
// If call.fail keeps adding the call to pendingCalls,
// the loop like for (int i = 0; i < pendingCalls.size(); i++) can't stop.
int pendingSize = pendingCalls.size();
// pendingCalls could be modified in this loop,
// hence using for-loop instead of iterator to avoid ConcurrentModificationException.
for (int i = 0; i < pendingSize; i++) {
Call call = pendingCalls.get(i);
// If the call is being retried, await the proper backoff before finding the node
if (now < call.nextAllowedTryMs) {
pollTimeout = Math.min(pollTimeout, call.nextAllowedTryMs - now);
} else if (maybeDrainPendingCall(call, now)) {
pendingIter.remove();
toRemove.add(call);
}
}
// Use remove instead of removeAll to avoid delete all matched elements
for (Call call : toRemove) {
pendingCalls.remove(call);
}
return pollTimeout;
}
@ -4701,6 +4714,8 @@ public class KafkaAdminClient extends AdminClient {
setPort(endpoint.port())));
return new AddRaftVoterRequest.Builder(
new AddRaftVoterRequestData().
setClusterId(options.clusterId().orElse(null)).
setTimeoutMs(timeoutMs).
setVoterId(voterId) .
setVoterDirectoryId(voterDirectoryId).
setListeners(listeners));
@ -4745,13 +4760,14 @@ public class KafkaAdminClient extends AdminClient {
RemoveRaftVoterRequest.Builder createRequest(int timeoutMs) {
return new RemoveRaftVoterRequest.Builder(
new RemoveRaftVoterRequestData().
setClusterId(options.clusterId().orElse(null)).
setVoterId(voterId) .
setVoterDirectoryId(voterDirectoryId));
}
@Override
void handleResponse(AbstractResponse response) {
AddRaftVoterResponse addResponse = (AddRaftVoterResponse) response;
RemoveRaftVoterResponse addResponse = (RemoveRaftVoterResponse) response;
if (addResponse.data().errorCode() != Errors.NONE.code()) {
ApiError error = new ApiError(
addResponse.data().errorCode(),
@ -4857,6 +4873,14 @@ public class KafkaAdminClient extends AdminClient {
return new GetNodesResult(future);
}
@Override
public UpdateGroupResult updateGroup(String groupId, UpdateGroupSpec groupSpec, UpdateGroupOptions options) {
SimpleAdminApiFuture<CoordinatorKey, Void> future = UpdateGroupHandler.newFuture(groupId);
UpdateGroupHandler handler = new UpdateGroupHandler(groupId, groupSpec, logContext);
invokeDriver(handler, future, options.timeoutMs);
return new UpdateGroupResult(future.get(CoordinatorKey.byGroupId(groupId)));
}
private <K, V> void invokeDriver(
AdminApiHandler<K, V> handler,
AdminApiFuture<K, V> future,
@ -4931,6 +4955,10 @@ public class KafkaAdminClient extends AdminClient {
return ListOffsetsRequest.EARLIEST_TIMESTAMP;
} else if (offsetSpec instanceof OffsetSpec.MaxTimestampSpec) {
return ListOffsetsRequest.MAX_TIMESTAMP;
} else if (offsetSpec instanceof OffsetSpec.EarliestLocalSpec) {
return ListOffsetsRequest.EARLIEST_LOCAL_TIMESTAMP;
} else if (offsetSpec instanceof OffsetSpec.LatestTieredSpec) {
return ListOffsetsRequest.LATEST_TIERED_TIMESTAMP;
}
return ListOffsetsRequest.LATEST_TIMESTAMP;
}

View File

@ -1,12 +1,20 @@
/*
* Copyright 2024, AutoMQ HK Limited.
* Copyright 2025, AutoMQ HK Limited.
*
* The use of this file is governed by the Business Source License,
* as detailed in the file "/LICENSE.S3Stream" included in this repository.
* 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
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
* 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.kafka.clients.admin;

View File

@ -26,6 +26,8 @@ public class OffsetSpec {
public static class EarliestSpec extends OffsetSpec { }
public static class LatestSpec extends OffsetSpec { }
public static class MaxTimestampSpec extends OffsetSpec { }
public static class EarliestLocalSpec extends OffsetSpec { }
public static class LatestTieredSpec extends OffsetSpec { }
public static class TimestampSpec extends OffsetSpec {
private final long timestamp;
@ -70,4 +72,23 @@ public class OffsetSpec {
return new MaxTimestampSpec();
}
/**
* Used to retrieve the local log start offset.
* Local log start offset is the offset of a log above which reads
* are guaranteed to be served from the disk of the leader broker.
* <br/>
* Note: When tiered Storage is not enabled, it behaves the same as retrieving the earliest timestamp offset.
*/
public static OffsetSpec earliestLocal() {
return new EarliestLocalSpec();
}
/**
* Used to retrieve the highest offset of data stored in remote storage.
* <br/>
* Note: When tiered storage is not enabled, we will return unknown offset.
*/
public static OffsetSpec latestTiered() {
return new LatestTieredSpec();
}
}

View File

@ -91,10 +91,8 @@ public class RaftVoterEndpoint {
@Override
public String toString() {
return "RaftVoterEndpoint" +
"(name=" + name +
", host=" + host +
", port=" + port +
")";
// enclose IPv6 hosts in square brackets for readability
String hostString = host.contains(":") ? "[" + host + "]" : host;
return name + "://" + hostString + ":" + port;
}
}

View File

@ -18,9 +18,21 @@ package org.apache.kafka.clients.admin;
import org.apache.kafka.common.annotation.InterfaceStability;
import java.util.Optional;
/**
* Options for {@link Admin#removeRaftVoter}.
*/
@InterfaceStability.Stable
public class RemoveRaftVoterOptions extends AbstractOptions<RemoveRaftVoterOptions> {
private Optional<String> clusterId = Optional.empty();
public RemoveRaftVoterOptions setClusterId(Optional<String> clusterId) {
this.clusterId = clusterId;
return this;
}
public Optional<String> clusterId() {
return clusterId;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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.kafka.clients.admin;
public class UpdateGroupOptions extends AbstractOptions<UpdateGroupOptions> {
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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.kafka.clients.admin;
import org.apache.kafka.common.KafkaFuture;
public class UpdateGroupResult extends AbstractOptions<UpdateGroupResult> {
private final KafkaFuture<Void> future;
UpdateGroupResult(final KafkaFuture<Void> future) {
this.future = future;
}
/**
* Return a future which succeeds if all the feature updates succeed.
*/
public KafkaFuture<Void> all() {
return future;
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2025, AutoMQ HK Limited.
*
* 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.kafka.clients.admin;
import java.util.Objects;
public class UpdateGroupSpec {
private String linkId;
private boolean promoted;
public UpdateGroupSpec linkId(String linkId) {
this.linkId = linkId;
return this;
}
public UpdateGroupSpec promoted(boolean promoted) {
this.promoted = promoted;
return this;
}
public String linkId() {
return linkId;
}
public boolean promoted() {
return promoted;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
UpdateGroupSpec spec = (UpdateGroupSpec) o;
return promoted == spec.promoted && Objects.equals(linkId, spec.linkId);
}
@Override
public int hashCode() {
return Objects.hash(linkId, promoted);
}
@Override
public String toString() {
return "UpdateGroupsSpec{" +
"linkId='" + linkId + '\'' +
", promoted=" + promoted +
'}';
}
}

Some files were not shown because too many files have changed in this diff Show More