Add alerting system client - #4
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (32)
📝 WalkthroughWalkthroughThis PR adds a new alerting client module and rule cache, updates alerting model and REST persistence types with ChangesAlerting Client, Cache and REST Changes
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant RulesCache
participant RulesClient
participant AlertingAPI
RulesCache->>RulesClient: getRevisionNumber()
RulesClient->>AlertingAPI: GET revision endpoint
AlertingAPI-->>RulesClient: revision number
RulesClient-->>RulesCache: revision number
RulesCache->>RulesClient: getAllActiveRules()
RulesClient->>AlertingAPI: GET active rules endpoint
AlertingAPI-->>RulesClient: List<Rule>
RulesClient-->>RulesCache: List<Rule>
RulesCache->>RulesCache: build RulesCacheSnapshot
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (5)
buildSrc/src/main/groovy/junit5-conventions.gradle (1)
7-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSame catalog/buildSrc duplication concern as
java-conventions.gradle.JUnit Jupiter versions are hardcoded to
5.10.0here, duplicating thejunitversion defined ingradle/libs.versions.toml. Currently in sync, but any future catalog bump won't automatically propagate here.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@buildSrc/src/main/groovy/junit5-conventions.gradle` around lines 7 - 9, The JUnit Jupiter dependencies in junit5-conventions.gradle are hardcoded, duplicating the version already defined in the version catalog. Update the dependency declarations in the junit5 conventions script to reference the catalog-managed junit version instead of 5.10.0, matching the approach used elsewhere such as java-conventions.gradle, so future version bumps propagate automatically.buildSrc/src/main/groovy/java-conventions.gradle (1)
15-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueLog4j versions hardcoded despite new catalog aliases.
gradle/libs.versions.tomlnow defineslog4j-slf4j2-implandlog4j-layout-template-jsonaliases with version2.21.0, but this convention script still hardcodes the version string directly instead of referencing the catalog. SincebuildSrcbuilds separately from the root project, this may be an unavoidable limitation (the version catalog isn't automatically accessible here), but it leaves two sources of truth for the log4j version that can silently drift. Consider wiring the catalog intobuildSrc(viasettings.gradlecatalog inclusion forbuildSrc) if not already done, or add a comment noting the manual sync requirement.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@buildSrc/src/main/groovy/java-conventions.gradle` around lines 15 - 16, The log4j dependency declarations in java-conventions.gradle still hardcode the version instead of using the new catalog aliases. Update the buildSrc setup to consume the version catalog for these dependencies if possible, or add an explicit comment near the dependency declarations noting that the log4j version must be kept in sync manually with the catalog aliases. Use the existing dependency entries for log4j-slf4j2-impl and log4j-layout-template-json as the place to make this change.common-libs/common-exceptions/src/main/java/ir/pathlens/client/ApiCallException.java (1)
6-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider adding a cause-preserving constructor.
Only a message constructor exists. If future call sites need to wrap lower-level exceptions (e.g.
IOExceptionfrom network calls), a(String message, Throwable cause)constructor would preserve the original stack trace.♻️ Optional cause-preserving constructor
public ApiCallException(String message) { super(message); } + + public ApiCallException(String message, Throwable cause) { + super(message, cause); + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@common-libs/common-exceptions/src/main/java/ir/pathlens/client/ApiCallException.java` around lines 6 - 12, Add a cause-preserving constructor to ApiCallException so it can wrap lower-level failures without losing the original stack trace. Update the ApiCallException class to include a constructor that accepts both a message and a Throwable cause, and delegate to the superclass the same way the existing message-only constructor does. Keep the existing message constructor unchanged so current call sites continue to work.alerting-system/model/src/main/java/ir/pathlens/alerting/model/RuleCreateDto.java (1)
32-34: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winUse
isBlank()to also catch whitespace-only titles.
isEmpty()won't catch a title of" ", which would bypass the default-name fallback.♻️ Proposed fix
- if (title == null || title.isEmpty()) { + if (title == null || title.isBlank()) { title = "NO NAME"; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@alerting-system/model/src/main/java/ir/pathlens/alerting/model/RuleCreateDto.java` around lines 32 - 34, The title fallback in RuleCreateDto only checks isEmpty(), so whitespace-only titles can slip through. Update the title validation in the RuleCreateDto constructor or setter logic to use isBlank() instead of isEmpty(), keeping the existing default assignment to "NO NAME" when title has no visible characters.alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/mappers/RuleMapper.java (1)
24-24: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winTruncation unit mismatch with entity lifecycle callbacks (MICROS here vs SECONDS in
RuleEntity).
RuleEntity.truncateTimestampsToSeconds()(@PrePersist/@PreUpdate) truncatescreatedAt/updatedAt/expiresAttoChronoUnit.SECONDSon every persist/update. Since that coarser truncation always runs afterward, theMICROStruncation done here is effectively discarded for persisted data —toDto's truncation on an already-persisted entity is a no-op, andfromDto'sMICROStruncation onexpiresAtgets further truncated toSECONDSright before/at insert. This inconsistency could mislead future maintainers into assuming microsecond precision survives persistence when it doesn't.Consider aligning both truncation units (e.g., use
SECONDSconsistently, matching what's actually persisted), or document whyMICROSis intentionally used here (e.g., for pre-persist comparison/testing purposes).Separately,
truncateToMicroshas no null guard — if ever called with anullLocalDateTime(e.g., on a transient, not-yet-persisted entity) it will NPE.♻️ Suggested consolidation
- private static java.time.LocalDateTime truncateToMicros(java.time.LocalDateTime dateTime) { - return dateTime.truncatedTo(ChronoUnit.MICROS); + private static LocalDateTime truncateToMicros(LocalDateTime dateTime) { + return dateTime == null ? null : dateTime.truncatedTo(ChronoUnit.SECONDS); }(also update
fromDto'sChronoUnit.MICROStoChronoUnit.SECONDS, and add thejava.time.LocalDateTimeimport)Also applies to: 35-46
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/mappers/RuleMapper.java` at line 24, Align the timestamp truncation in RuleMapper with RuleEntity’s lifecycle behavior: the MICROS truncation in fromDto/toDto is misleading because RuleEntity.truncateTimestampsToSeconds() ultimately persists seconds precision. Update the mapper to use ChronoUnit.SECONDS consistently for expiresAt (and any related truncation paths), or explicitly document why microseconds are only temporary. Also make truncateToMicros null-safe by guarding against null LocalDateTime inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@alerting-system/client/build.gradle`:
- Around line 5-9: The `:alerting-system:client` module is leaking
`ir.pathlens.alerting.model.Rule` through `RulesClient.getAllActiveRules()`, so
`:alerting-system:model` must be exposed on the public API instead of being
internal. Update the dependency declaration in the `dependencies` block so the
`project(':alerting-system:model')` entry is an `api` dependency, keeping
`RulesClient`’s return type available to downstream consumers.
In
`@alerting-system/client/src/main/java/ir/pathlens/alerting/client/RulesCache.java`:
- Around line 56-68: The immutable snapshot contract in RulesCache is broken
because RulesCacheSnapshot still exposes mutable Set<UUID> values via
getRulesIdsByIdentity(). Update the snapshot creation in RulesCache and the
RulesCacheSnapshot accessors so the grouped rule-id sets are defensively copied
into unmodifiable sets before being stored, not just wrapped by Map.copyOf().
Keep the fix centered around the updatedRuleCache build path and the
getRulesIdsByIdentity() API so callers cannot mutate internal cache state.
- Around line 94-111: The background loop in runSyncLoop only handles
ApiCallException, so any other RuntimeException from sync() or the initial sleep
can kill the worker thread while running stays true. Update runSyncLoop to catch
unexpected runtime failures as well, log them with context, and ensure the loop
does not die silently; also review the initialDelay calculation in runSyncLoop
and the RulesCache constructor validation so invalid minInitialDelayInMillis
values cannot reach TimeUnit.MILLISECONDS.sleep.
In
`@alerting-system/client/src/main/java/ir/pathlens/alerting/client/RulesClient.java`:
- Around line 22-31: `RulesClient` is creating a JAX-RS `Client` without any
lifecycle cleanup, so the underlying resources are leaked. Update the
`RulesClient` class to implement `AutoCloseable` or `Closeable`, add a `close()`
method that delegates to `client.close()`, and make sure any callers such as
`RuleControllerTest` or other instantiations use try-with-resources or otherwise
close the instance after use.
In
`@alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/configs/TargetLogKafkaConsumer.java`:
- Around line 39-41: The TargetLogKafkaConsumer setup leaves autoOffsetReset
implicit when config.autoOffsetReset() is null, which can default to latest and
skip backlog on first start. Update the ConsumerConfig property assignment in
TargetLogKafkaConsumer to always set an explicit default, using earliest unless
the bound config already provides a value, so the consumer behavior is clear and
consistent.
In
`@alerting-system/rest/src/test/java/ir/pathlens/alerting/rest/controllers/RuleControllerTest.java`:
- Around line 98-114: `RulesCache` background executor threads are leaked in the
test methods that call `submitBackgroundTask()` without cleanup. Update
`testCacheContainsCreatedRules` and `testIdenticalIdentitiesInCache` to manage
the `RulesCache` lifecycle via try-with-resources (or ensure `close()` is called
in a shared teardown), using the `RulesCache` AutoCloseable contract to stop the
background task after assertions complete.
---
Nitpick comments:
In
`@alerting-system/model/src/main/java/ir/pathlens/alerting/model/RuleCreateDto.java`:
- Around line 32-34: The title fallback in RuleCreateDto only checks isEmpty(),
so whitespace-only titles can slip through. Update the title validation in the
RuleCreateDto constructor or setter logic to use isBlank() instead of isEmpty(),
keeping the existing default assignment to "NO NAME" when title has no visible
characters.
In
`@alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/mappers/RuleMapper.java`:
- Line 24: Align the timestamp truncation in RuleMapper with RuleEntity’s
lifecycle behavior: the MICROS truncation in fromDto/toDto is misleading because
RuleEntity.truncateTimestampsToSeconds() ultimately persists seconds precision.
Update the mapper to use ChronoUnit.SECONDS consistently for expiresAt (and any
related truncation paths), or explicitly document why microseconds are only
temporary. Also make truncateToMicros null-safe by guarding against null
LocalDateTime inputs.
In `@buildSrc/src/main/groovy/java-conventions.gradle`:
- Around line 15-16: The log4j dependency declarations in
java-conventions.gradle still hardcode the version instead of using the new
catalog aliases. Update the buildSrc setup to consume the version catalog for
these dependencies if possible, or add an explicit comment near the dependency
declarations noting that the log4j version must be kept in sync manually with
the catalog aliases. Use the existing dependency entries for log4j-slf4j2-impl
and log4j-layout-template-json as the place to make this change.
In `@buildSrc/src/main/groovy/junit5-conventions.gradle`:
- Around line 7-9: The JUnit Jupiter dependencies in junit5-conventions.gradle
are hardcoded, duplicating the version already defined in the version catalog.
Update the dependency declarations in the junit5 conventions script to reference
the catalog-managed junit version instead of 5.10.0, matching the approach used
elsewhere such as java-conventions.gradle, so future version bumps propagate
automatically.
In
`@common-libs/common-exceptions/src/main/java/ir/pathlens/client/ApiCallException.java`:
- Around line 6-12: Add a cause-preserving constructor to ApiCallException so it
can wrap lower-level failures without losing the original stack trace. Update
the ApiCallException class to include a constructor that accepts both a message
and a Throwable cause, and delegate to the superclass the same way the existing
message-only constructor does. Keep the existing message constructor unchanged
so current call sites continue to work.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2a15d72a-388b-4e71-93d4-0901f177dbed
📒 Files selected for processing (32)
.idea/gradle.xmlalerting-system/client/build.gradlealerting-system/client/src/main/java/ir/pathlens/alerting/client/RulesCache.javaalerting-system/client/src/main/java/ir/pathlens/alerting/client/RulesClient.javaalerting-system/model/build.gradlealerting-system/model/src/main/java/ir/pathlens/alerting/model/Notification.javaalerting-system/model/src/main/java/ir/pathlens/alerting/model/NotificationFilter.javaalerting-system/model/src/main/java/ir/pathlens/alerting/model/Rule.javaalerting-system/model/src/main/java/ir/pathlens/alerting/model/RuleCreateDto.javaalerting-system/rest/build.gradlealerting-system/rest/src/main/java/ir/pathlens/alerting/rest/configs/TargetLogKafkaConsumer.javaalerting-system/rest/src/main/java/ir/pathlens/alerting/rest/controller/RuleController.javaalerting-system/rest/src/main/java/ir/pathlens/alerting/rest/entity/LogEntity.javaalerting-system/rest/src/main/java/ir/pathlens/alerting/rest/entity/NotificationEntity.javaalerting-system/rest/src/main/java/ir/pathlens/alerting/rest/entity/RuleEntity.javaalerting-system/rest/src/main/java/ir/pathlens/alerting/rest/mappers/RuleMapper.javaalerting-system/rest/src/test/java/ir/pathlens/alerting/rest/controllers/LogControllerTest.javaalerting-system/rest/src/test/java/ir/pathlens/alerting/rest/controllers/NotificationControllerTest.javaalerting-system/rest/src/test/java/ir/pathlens/alerting/rest/controllers/RuleControllerTest.javaalerting-system/rest/src/test/java/ir/pathlens/alerting/rest/service/TargetLogBatchPersisterTest.javaalerting-system/rest/src/test/java/ir/pathlens/alerting/rest/util/CommonConfigs.javaalerting-system/rest/src/test/java/ir/pathlens/alerting/rest/util/CommonKafkaConfigs.javaalerting-system/rest/src/test/resources/application.ymlbuildSrc/src/main/groovy/java-conventions.gradlebuildSrc/src/main/groovy/junit5-conventions.gradlecommon-libs/common-exceptions/build.gradlecommon-libs/common-exceptions/src/main/java/ir/pathlens/client/ApiCallException.javacommon-libs/location-utils/build.gradlecommon-libs/test-extensions/build.gradlecommon-libs/test-extensions/src/main/java/ir/pathlens/extension/postgresql/SpringCommonPostgresConfigs.javagradle/libs.versions.tomlsettings.gradle
💤 Files with no reviewable changes (1)
- common-libs/test-extensions/build.gradle
| private void runSyncLoop() { | ||
| while (running) { | ||
| try { | ||
| long initialDelay = random.nextInt(minInitialDelayInMillis, maxInitialDelayInMillis); | ||
| try { | ||
| TimeUnit.MILLISECONDS.sleep(initialDelay); | ||
| } catch (InterruptedException e) { | ||
| logger.error("Interrupted during initial delay", e); | ||
| Thread.currentThread().interrupt(); | ||
|
|
||
| return; | ||
| } | ||
| sync(); | ||
| } catch (ApiCallException ex) { | ||
| logger.error("Unable to sync rules", ex); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Background sync loop dies silently on any non-ApiCallException failure.
runSyncLoop only catches ApiCallException. Any other RuntimeException from sync() — including IllegalArgumentException from TimeUnit.MILLISECONDS.sleep(initialDelay) if minInitialDelayInMillis is ever negative (unvalidated in the constructor, only the max > min relation is checked) — will propagate, terminate the single worker thread, yet running remains true. The cache then silently stops refreshing forever with no visible signal to callers.
🔧 Proposed fix
sync();
} catch (ApiCallException ex) {
logger.error("Unable to sync rules", ex);
+ } catch (RuntimeException ex) {
+ logger.error("Unexpected error while syncing rules; continuing loop", ex);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private void runSyncLoop() { | |
| while (running) { | |
| try { | |
| long initialDelay = random.nextInt(minInitialDelayInMillis, maxInitialDelayInMillis); | |
| try { | |
| TimeUnit.MILLISECONDS.sleep(initialDelay); | |
| } catch (InterruptedException e) { | |
| logger.error("Interrupted during initial delay", e); | |
| Thread.currentThread().interrupt(); | |
| return; | |
| } | |
| sync(); | |
| } catch (ApiCallException ex) { | |
| logger.error("Unable to sync rules", ex); | |
| } | |
| } | |
| } | |
| private void runSyncLoop() { | |
| while (running) { | |
| try { | |
| long initialDelay = random.nextInt(minInitialDelayInMillis, maxInitialDelayInMillis); | |
| try { | |
| TimeUnit.MILLISECONDS.sleep(initialDelay); | |
| } catch (InterruptedException e) { | |
| logger.error("Interrupted during initial delay", e); | |
| Thread.currentThread().interrupt(); | |
| return; | |
| } | |
| sync(); | |
| } catch (ApiCallException ex) { | |
| logger.error("Unable to sync rules", ex); | |
| } catch (RuntimeException ex) { | |
| logger.error("Unexpected error while syncing rules; continuing loop", ex); | |
| } | |
| } | |
| } |
🧰 Tools
🪛 PMD (7.25.0)
[Low] 108-108: InvalidLogMessageFormat (Error Prone): Too many arguments, expected 0 argument but found 1
(InvalidLogMessageFormat (Error Prone))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@alerting-system/client/src/main/java/ir/pathlens/alerting/client/RulesCache.java`
around lines 94 - 111, The background loop in runSyncLoop only handles
ApiCallException, so any other RuntimeException from sync() or the initial sleep
can kill the worker thread while running stays true. Update runSyncLoop to catch
unexpected runtime failures as well, log them with context, and ensure the loop
does not die silently; also review the initialDelay calculation in runSyncLoop
and the RulesCache constructor validation so invalid minInitialDelayInMillis
values cannot reach TimeUnit.MILLISECONDS.sleep.
| if (config.autoOffsetReset() != null) { | ||
| props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, config.autoOffsetReset()); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect KafkaConsumerConfig definition and its usages/defaults for autoOffsetReset
ast-grep run --pattern 'record KafkaConsumerConfig($$$) { $$$ }' --lang java
rg -nP 'autoOffsetReset' --type=java -C3
rg -nP 'auto-offset-reset|autoOffsetReset' --type=yaml -C3Repository: alishazaee/PathLens
Length of output: 218
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the target file and nearby symbols
ast-grep outline alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/configs/TargetLogKafkaConsumer.java --view expanded
# Find the config type and any references/defaults for autoOffsetReset across the repo
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' 'autoOffsetReset|auto-offset-reset|AUTO_OFFSET_RESET_CONFIG' .
# Locate likely config classes for Kafka settings
fd -a 'KafkaConsumerConfig.java' .
fd -a '*Kafka*Config*.java' .
fd -a '*application*.yml' .
fd -a '*application*.yaml' .Repository: alishazaee/PathLens
Length of output: 1353
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the Kafka consumer config record and the consumer creation code
sed -n '1,120p' alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/configs/KafkaConsumerConfig.java
printf '\n---\n'
sed -n '1,140p' alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/configs/TargetLogKafkaConsumer.java
printf '\n---\n'
# Check runtime config files for any default auto-offset-reset values
fd -a 'application.yml' alerting-system/rest
fd -a 'application.yaml' alerting-system/rest
printf '\n---\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' 'auto-offset-reset|autoOffsetReset' alerting-system/rest/src/main/resources alerting-system/rest/src/test/resources
# Check if the config object is annotated/bound in a way that makes the field required or optional
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' '`@ConfigurationProperties`|`@ConstructorBinding`|`@DefaultValue`|record KafkaConsumerConfig|class KafkaConsumerConfig' alerting-system/rest/src/main/javaRepository: alishazaee/PathLens
Length of output: 3218
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect application config binding and any main-resource defaults
sed -n '1,120p' alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/configs/ApplicationConfig.java
printf '\n---\n'
find alerting-system/rest/src/main/resources -maxdepth 2 -type f \( -name 'application.yml' -o -name 'application.yaml' -o -name 'application.properties' \) -print -exec sh -c 'echo "---"; sed -n "1,220p" "$1"' _ {} \;
printf '\n---\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' 'auto-offset-reset|autoOffsetReset|earliest|latest' alerting-system/rest/src/main/resources alerting-system/rest/src/main/javaRepository: alishazaee/PathLens
Length of output: 1198
Set an explicit autoOffsetReset default
config.autoOffsetReset() is nullable, so omitting it falls back to Kafka’s latest default and can skip existing messages on first start. If this consumer should read backlog by default, make earliest explicit here or in the bound config.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@alerting-system/rest/src/main/java/ir/pathlens/alerting/rest/configs/TargetLogKafkaConsumer.java`
around lines 39 - 41, The TargetLogKafkaConsumer setup leaves autoOffsetReset
implicit when config.autoOffsetReset() is null, which can default to latest and
skip backlog on first start. Update the ConsumerConfig property assignment in
TargetLogKafkaConsumer to always set an explicit default, using earliest unless
the bound config already provides a value, so the consumer behavior is clear and
consistent.
7c5c26d to
5d47e65
Compare
Summary by CodeRabbit